From 1c7992ec752bcb2363b9fba5e7d5a99f29887ece Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 1 Jul 2015 02:00:16 +0300 Subject: [PATCH 001/137] Fixed running Python plugin tests in IntelliJ The tests used to depend on python-community-tests, so some components from IdeaPlugin.xml didn't get initialized. Added a dependency on Python Community plugin resources for the correct plugin.xml. --- .../com/jetbrains/jython/PyJythonHighlightingTest.java | 4 ++-- .../com/jetbrains/jython/PyToJavaResolveTest.java | 4 ++-- python/python-plugin-tests.iml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/python/pluginTestSrc/com/jetbrains/jython/PyJythonHighlightingTest.java b/python/pluginTestSrc/com/jetbrains/jython/PyJythonHighlightingTest.java index 4ddef37bca86..744ee5cd98c7 100644 --- a/python/pluginTestSrc/com/jetbrains/jython/PyJythonHighlightingTest.java +++ b/python/pluginTestSrc/com/jetbrains/jython/PyJythonHighlightingTest.java @@ -16,7 +16,7 @@ package com.jetbrains.jython; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; -import com.jetbrains.python.PythonTestUtil; +import com.jetbrains.python.PythonHelpersLocator; import com.jetbrains.python.inspections.PyCallingNonCallableInspection; import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection; @@ -41,6 +41,6 @@ public class PyJythonHighlightingTest extends LightCodeInsightFixtureTestCase { @Override protected String getTestDataPath() { - return PythonTestUtil.getTestDataPath() + "/highlighting/jython/"; + return PythonHelpersLocator.getPythonCommunityPath() + "/testData/highlighting/jython/"; } } diff --git a/python/pluginTestSrc/com/jetbrains/jython/PyToJavaResolveTest.java b/python/pluginTestSrc/com/jetbrains/jython/PyToJavaResolveTest.java index a733592e0fef..1d9602795fa7 100644 --- a/python/pluginTestSrc/com/jetbrains/jython/PyToJavaResolveTest.java +++ b/python/pluginTestSrc/com/jetbrains/jython/PyToJavaResolveTest.java @@ -18,7 +18,7 @@ package com.jetbrains.jython; import com.intellij.psi.*; import com.intellij.testFramework.ResolveTestCase; import com.intellij.testFramework.TestDataPath; -import com.jetbrains.python.PythonTestUtil; +import com.jetbrains.python.PythonHelpersLocator; import junit.framework.Assert; /** @@ -87,6 +87,6 @@ public class PyToJavaResolveTest extends ResolveTestCase { @Override protected String getTestDataPath() { - return PythonTestUtil.getTestDataPath() + "/resolve/pyToJava/"; + return PythonHelpersLocator.getPythonCommunityPath() + "/testData/resolve/pyToJava/"; } } diff --git a/python/python-plugin-tests.iml b/python/python-plugin-tests.iml index 37a5ca500a58..7c666a1dd0a6 100644 --- a/python/python-plugin-tests.iml +++ b/python/python-plugin-tests.iml @@ -14,10 +14,10 @@ - + From c80b8263c77422f41a5977e551e8d70fd4a86dbb Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 1 Jul 2015 13:10:43 +0300 Subject: [PATCH 002/137] Added a test for PY-14454 --- .../importSubModuleDunderAll/ImportSubModuleDunderAll.py | 4 ++++ .../multiFile/importSubModuleDunderAll/pkg1/__init__.py | 1 + .../resolve/multiFile/importSubModuleDunderAll/pkg1/m1.py | 2 ++ .../testSrc/com/jetbrains/python/PyMultiFileResolveTest.java | 3 +++ 4 files changed, 10 insertions(+) create mode 100644 python/testData/resolve/multiFile/importSubModuleDunderAll/ImportSubModuleDunderAll.py create mode 100644 python/testData/resolve/multiFile/importSubModuleDunderAll/pkg1/__init__.py create mode 100644 python/testData/resolve/multiFile/importSubModuleDunderAll/pkg1/m1.py diff --git a/python/testData/resolve/multiFile/importSubModuleDunderAll/ImportSubModuleDunderAll.py b/python/testData/resolve/multiFile/importSubModuleDunderAll/ImportSubModuleDunderAll.py new file mode 100644 index 000000000000..e5ee56c5fc66 --- /dev/null +++ b/python/testData/resolve/multiFile/importSubModuleDunderAll/ImportSubModuleDunderAll.py @@ -0,0 +1,4 @@ +from pkg1 import m1 + +print(m1) +# diff --git a/python/testData/resolve/multiFile/importSubModuleDunderAll/pkg1/__init__.py b/python/testData/resolve/multiFile/importSubModuleDunderAll/pkg1/__init__.py new file mode 100644 index 000000000000..a6d9575178da --- /dev/null +++ b/python/testData/resolve/multiFile/importSubModuleDunderAll/pkg1/__init__.py @@ -0,0 +1 @@ +__all__ = ['m1'] diff --git a/python/testData/resolve/multiFile/importSubModuleDunderAll/pkg1/m1.py b/python/testData/resolve/multiFile/importSubModuleDunderAll/pkg1/m1.py new file mode 100644 index 000000000000..9332a2735b6a --- /dev/null +++ b/python/testData/resolve/multiFile/importSubModuleDunderAll/pkg1/m1.py @@ -0,0 +1,2 @@ +def foo(): + pass diff --git a/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java b/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java index 97060e8922e3..fd5ee281e4a8 100644 --- a/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java +++ b/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java @@ -407,4 +407,7 @@ public class PyMultiFileResolveTest extends PyMultiFileResolveTestCase { assertResolvesTo(PyFile.class, "z.py"); } + public void testImportSubModuleDunderAll() { + assertResolvesTo(PyFile.class, "m1.py"); + } } \ No newline at end of file From ce93923e34caa685725a92cc54594d7d3d7c57ee Mon Sep 17 00:00:00 2001 From: "Vladimir.Orlov" Date: Wed, 1 Jul 2015 15:11:28 +0300 Subject: [PATCH 003/137] PyCharm EDU jdk bundled linux artifact. --- python/edu/build/pycharm_edu_build.gant | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/python/edu/build/pycharm_edu_build.gant b/python/edu/build/pycharm_edu_build.gant index 6fb0e074345d..e1ed6b8f8aaf 100644 --- a/python/edu/build/pycharm_edu_build.gant +++ b/python/edu/build/pycharm_edu_build.gant @@ -203,6 +203,10 @@ public layoutEducational(String classesPath, Set usedJars) { String tarRoot = isEap() ? "pycharm-edu-$buildNumber" : "pycharm-edu-${p("component.version.major")}.${p("component.version.minor")}" buildTarGz(tarRoot, "$paths.artifacts/pycharm${buildName}.tar", [paths.distAll, paths.distUnix]) + if (p("jdk.bundled.linux") != "false") { + buildTarGz(tarRoot, "$paths.artifacts/pycharm${buildName}-jdk-bundled.tar", [paths.distAll, paths.distUnix, "${paths.sandbox}/bundled.linux.jdk"], ["jre/bin/*"]) + } + String macAppRoot = isEap() ? "PyCharm Educational ${p("component.version.major")}.${p("component.version.minor")} EAP.app/Contents" : "PyCharm Educational.app/Contents" buildMacZip(macAppRoot, "${paths.artifacts}/pycharm${buildName}.sit", [paths.distAll], paths.distMac) From 648fe87a05b590e5a8f975d9ffa9b041f428ab1c Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 1 Jul 2015 15:51:54 +0300 Subject: [PATCH 004/137] Prefer submodules to '__all__' defined in the package (PY-14454) We resolved references to attributes / submodules of a package to '__all__' if we found the name of the attribute in '__all__'. That is OK as a fallback if there is no appropriate submodule, but in other cases we now resolve the reference to a submodule, not to '__all__'. --- .../python/psi/resolve/ResolveImportUtil.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java index 7f2596e9c0c6..4889a02c01ff 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java +++ b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java @@ -257,8 +257,7 @@ public class ResolveImportUtil { } /** - * Tries to find referencedName under the parent element. Used to resolve any names that look imported. - * Parent might happen to be a PyFile(__init__.py), then it is treated both as a file and as ist base dir. + * Tries to find referencedName under the parent element. * * @param parent element under which to look for referenced name; if null, null is returned. * @param referencedName which name to look for. @@ -266,17 +265,15 @@ public class ResolveImportUtil { * @param fileOnly if true, considers only a PsiFile child as a valid result; non-file hits are ignored. * @param checkForPackage if true, directories are returned only if they contain __init__.py * @return the element the referencedName resolves to, or null. - * @todo: Honor module's __all__ value. - * @todo: Honor package's __path__ value (hard). */ @Nullable public static PsiElement resolveChild(@Nullable final PsiElement parent, @NotNull final String referencedName, @Nullable final PsiFile containingFile, boolean fileOnly, boolean checkForPackage) { PsiDirectory dir = null; PsiElement resultElement = null; - PsiElement possibleResult = null; final PyResolveContext resolveContext = PyResolveContext.defaultContext(); if (parent instanceof PyFileImpl) { + PsiElement possibleResult = null; if (PyNames.INIT_DOT_PY.equals(((PyFile)parent).getName())) { // gobject does weird things like '_gobject = sys.modules['gobject._gobject'], so it's preferable to look at // files before looking at names exported from __init__.py @@ -294,11 +291,15 @@ public class ResolveImportUtil { resultElement = moduleMember; } if (resultElement != null && !PyUtil.instanceOf(resultElement, PsiFile.class, PsiDirectory.class) && - PsiTreeUtil.getStubOrPsiParentOfType(resultElement, PyExceptPart.class) == null) { + PsiTreeUtil.getStubOrPsiParentOfType(resultElement, PyExceptPart.class) == null && !isDunderAll(resultElement)) { return resultElement; } if (possibleResult != null) return possibleResult; + + if (resultElement != null) { + return resultElement; + } } else if (parent instanceof PsiDirectory) { dir = (PsiDirectory)parent; @@ -325,7 +326,11 @@ public class ResolveImportUtil { } } } - return resultElement; + return null; + } + + private static boolean isDunderAll(@NotNull PsiElement element) { + return (element instanceof PyElement) && PyNames.ALL.equals(((PyElement)element).getName()); } @Nullable From a6e287397357faa53d874cf1165db9a00e49c9b6 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 1 Jul 2015 16:01:30 +0300 Subject: [PATCH 005/137] Removed unnecessary comments --- .../com/jetbrains/python/psi/resolve/ResolveImportUtil.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java index 4889a02c01ff..b156b608c6bb 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java +++ b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java @@ -275,14 +275,10 @@ public class ResolveImportUtil { if (parent instanceof PyFileImpl) { PsiElement possibleResult = null; if (PyNames.INIT_DOT_PY.equals(((PyFile)parent).getName())) { - // gobject does weird things like '_gobject = sys.modules['gobject._gobject'], so it's preferable to look at - // files before looking at names exported from __init__.py dir = ((PyFile)parent).getContainingDirectory(); possibleResult = resolveInDirectory(referencedName, containingFile, dir, fileOnly, checkForPackage); } - // OTOH, quite often a module named foo exports a class or function named foo, which is used as a fallback - // by a module one level higher (e.g. curses.set_key). Prefer it to submodule if possible. final PyModuleType moduleType = new PyModuleType((PyFile)parent); final List results = moduleType.resolveMember(referencedName, null, AccessDirection.READ, resolveContext); @@ -315,7 +311,6 @@ public class ResolveImportUtil { } if (dir != null) { final PsiElement result = resolveInDirectory(referencedName, containingFile, dir, fileOnly, checkForPackage); - //if (fileOnly && ! (result instanceof PsiFile) && ! (result instanceof PsiDirectory)) return null; if (result != null) { return result; } From 815f30f45551390a8ecb33467660753c0a1fa36c Mon Sep 17 00:00:00 2001 From: Denis Fokin Date: Wed, 1 Jul 2015 16:03:13 +0300 Subject: [PATCH 006/137] IDEA-142156 Project leak on SheetController$2 --- .../src/com/intellij/ui/messages/SheetController.java | 4 ++++ .../src/com/intellij/ui/messages/SheetMessage.java | 1 + 2 files changed, 5 insertions(+) diff --git a/platform/platform-impl/src/com/intellij/ui/messages/SheetController.java b/platform/platform-impl/src/com/intellij/ui/messages/SheetController.java index 86edcab49f22..ab68db206d15 100755 --- a/platform/platform-impl/src/com/intellij/ui/messages/SheetController.java +++ b/platform/platform-impl/src/com/intellij/ui/messages/SheetController.java @@ -492,4 +492,8 @@ public class SheetController { public String getResult() { return myResult; } + + public void dispose() { + mySheetPanel.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0)); + } } diff --git a/platform/platform-impl/src/com/intellij/ui/messages/SheetMessage.java b/platform/platform-impl/src/com/intellij/ui/messages/SheetMessage.java index e9627386eb88..0b2eb2ecd1a2 100755 --- a/platform/platform-impl/src/com/intellij/ui/messages/SheetMessage.java +++ b/platform/platform-impl/src/com/intellij/ui/messages/SheetMessage.java @@ -228,6 +228,7 @@ public class SheetMessage { if (restoreFullScreenButton) { FullScreenUtilities.setWindowCanFullScreen(myParent, true); } + myController.dispose(); myWindow.dispose(); } } From 7e7210381735cda65e141960ecec76a7f09ec7f3 Mon Sep 17 00:00:00 2001 From: Dennis Ushakov Date: Wed, 1 Jul 2015 13:06:53 +0200 Subject: [PATCH 007/137] don't fail on null file path #IDEA-142041 fixed --- .../plugincomponents/SettingsProviderComponent.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/editorconfig/src/org/editorconfig/plugincomponents/SettingsProviderComponent.java b/plugins/editorconfig/src/org/editorconfig/plugincomponents/SettingsProviderComponent.java index 0d2c59e931cc..1655e5f3105e 100644 --- a/plugins/editorconfig/src/org/editorconfig/plugincomponents/SettingsProviderComponent.java +++ b/plugins/editorconfig/src/org/editorconfig/plugincomponents/SettingsProviderComponent.java @@ -31,6 +31,8 @@ public class SettingsProviderComponent { } public List getOutPairs(Project project, String filePath) { + if (filePath == null) return Collections.emptyList(); + final List outPairs; try { final Set rootDirs = getRootDirs(project); From 6abee65b9c72b844007a336c85835f18e70b6c90 Mon Sep 17 00:00:00 2001 From: Pavel Fatin Date: Wed, 1 Jul 2015 15:28:29 +0200 Subject: [PATCH 008/137] DocumentationManager: don't depend on ActionManagerEx unnecessarily (relevant for Upsource) --- .../documentation/DocumentationManager.java | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java index 995110c7d172..0a1d25315b42 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java @@ -33,7 +33,6 @@ import com.intellij.lang.Language; import com.intellij.lang.LanguageDocumentation; import com.intellij.lang.documentation.*; import com.intellij.openapi.actionSystem.*; -import com.intellij.openapi.actionSystem.ex.ActionManagerEx; import com.intellij.openapi.actionSystem.ex.AnActionListener; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; @@ -96,7 +95,7 @@ public class DocumentationManager extends DockablePopupManager ORIGINAL_ELEMENT_KEY = Key.create("Original element"); - private final ActionManagerEx myActionManagerEx; + private final ActionManager myActionManager; private final TargetElementUtil myTargetElementUtil; @@ -161,9 +160,9 @@ public class DocumentationManager extends DockablePopupManager> actions = ContainerUtil.newSmartList(); - AnAction quickDocAction = ActionManagerEx.getInstanceEx().getAction(IdeActions.ACTION_QUICK_JAVADOC); + AnAction quickDocAction = ActionManager.getInstance().getAction(IdeActions.ACTION_QUICK_JAVADOC); for (Shortcut shortcut : quickDocAction.getShortcutSet().getShortcuts()) { if (!(shortcut instanceof KeyboardShortcut)) continue; actions.add(Pair.create(actionListener, ((KeyboardShortcut)shortcut).getFirstKeyStroke())); From 2328919c0e6698031d84a3c539e4ea7506cfc7bb Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Wed, 1 Jul 2015 16:06:09 +0200 Subject: [PATCH 009/137] - use fileId for updateSingleIndex - extracted common method --- .../util/indexing/FileBasedIndexImpl.java | 64 ++++++++++--------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java index 52f6d4ae20ea..e327cf9e2d6f 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java @@ -1623,6 +1623,7 @@ public class FileBasedIndexImpl extends FileBasedIndex { try { PsiFile psiFile = null; FileContentImpl fc = null; + int inputId = -1; final List> affectedIndexCandidates = getAffectedIndexCandidates(file); //noinspection ForLoopReplaceableByForEach @@ -1658,11 +1659,12 @@ public class FileBasedIndexImpl extends FileBasedIndex { psiFile = content.getUserData(IndexingDataKeys.PSI_FILE); initFileContent(fc, project, psiFile); + inputId = Math.abs(getFileId(file)); } try { ProgressManager.checkCanceled(); - updateSingleIndex(indexId, file, fc); + updateSingleIndex(indexId, inputId, fc); } catch (ProcessCanceledException e) { cleanFileContent(fc, psiFile); @@ -1714,14 +1716,13 @@ public class FileBasedIndexImpl extends FileBasedIndex { fc.putUserData(IndexingDataKeys.PROJECT, project); } - private void updateSingleIndex(@NotNull ID indexId, @NotNull final VirtualFile file, @Nullable FileContent currentFC) + private void updateSingleIndex(@NotNull ID indexId, final int inputId, @Nullable FileContent currentFC) throws StorageException { if (ourRebuildStatus.get(indexId).get() == REQUIRES_REBUILD) { return; // the index is scheduled for rebuild, no need to update } myLocalModCount++; - final int inputId = Math.abs(getFileId(file)); final UpdatableIndex index = getIndex(indexId); assert index != null; @@ -1736,7 +1737,7 @@ public class FileBasedIndexImpl extends FileBasedIndex { try { scheduleUpdate(indexId, createUpdateComputableWithBufferingDisabled(update), - createIndexedStampUpdateRunnable(indexId, file, currentFC != null) + createIndexedStampUpdateRunnable(indexId, inputId, currentFC != null) ); } catch (RuntimeException exception) { Throwable causeToRebuildIndex = getCauseToRebuildIndex(exception); @@ -1753,21 +1754,18 @@ public class FileBasedIndexImpl extends FileBasedIndex { @NotNull private Runnable createIndexedStampUpdateRunnable(@NotNull final ID indexId, - @NotNull final VirtualFile file, + final int fileId, final boolean hasContent) { return new Runnable() { @Override public void run() { - if (file.isValid()) { - int fileId = getIdMaskingNonIdBasedFile(file); - if (hasContent) { - IndexingStamp.setFileIndexedStateCurrent(fileId, indexId); - } - else { - IndexingStamp.setFileIndexedStateUnindexed(fileId, indexId); - } - if (myNotRequiringContentIndices.contains(indexId)) IndexingStamp.flushCache(fileId); + if (hasContent) { + IndexingStamp.setFileIndexedStateCurrent(fileId, indexId); } + else { + IndexingStamp.setFileIndexedStateUnindexed(fileId, indexId); + } + if (myNotRequiringContentIndices.contains(indexId)) IndexingStamp.flushCache(fileId); } }; } @@ -1934,13 +1932,15 @@ public class FileBasedIndexImpl extends FileBasedIndex { boolean fileIsDirectory = file.isDirectory(); if (!contentChange) { FileContent fileContent = null; + int inputId = -1; for (ID indexId : fileIsDirectory ? myIndicesForDirectories : myNotRequiringContentIndices) { if (getInputFilter(indexId).acceptInput(file)) { try { if (fileContent == null) { fileContent = new FileContentImpl(file); + inputId = Math.abs(getFileId(file)); } - updateSingleIndex(indexId, file, fileContent); + updateSingleIndex(indexId, inputId, fileContent); } catch (StorageException e) { LOG.info(e); @@ -2035,16 +2035,16 @@ public class FileBasedIndexImpl extends FileBasedIndex { private void invalidateIndicesForFile(@NotNull final VirtualFile file, boolean markForReindex) { cleanProcessedFlag(file); - IndexingStamp.flushCache(file); - final int fileId = getIdMaskingNonIdBasedFile(file); + final int fileId = Math.abs(getIdMaskingNonIdBasedFile(file)); + IndexingStamp.flushCache(fileId); List> nontrivialFileIndexedStates = IndexingStamp.getNontrivialFileIndexedStates(fileId); if (!markForReindex) { // markForReindex really means content changed for (ID indexId : nontrivialFileIndexedStates) { if (myNotRequiringContentIndices.contains(indexId)) { try { - updateSingleIndex(indexId, file, null); + updateSingleIndex(indexId, fileId, null); } catch (StorageException e) { LOG.info(e); @@ -2080,19 +2080,19 @@ public class FileBasedIndexImpl extends FileBasedIndex { myFutureInvalidations.offer(new InvalidationTask(file) { @Override public void run() { - removeFileDataFromIndices(finalFileIndexedStatesToUpdate, getSubj()); + removeFileDataFromIndices(finalFileIndexedStatesToUpdate, fileId); } }); } - IndexingStamp.flushCache(file); + IndexingStamp.flushCache(fileId); } - private void removeFileDataFromIndices(@NotNull Collection> affectedIndices, @NotNull VirtualFile file) { + private void removeFileDataFromIndices(@NotNull Collection> affectedIndices, int inputId) { Throwable unexpectedError = null; for (ID indexId : affectedIndices) { try { - updateSingleIndex(indexId, file, null); + updateSingleIndex(indexId, inputId, null); } catch (StorageException e) { LOG.info(e); @@ -2108,7 +2108,7 @@ public class FileBasedIndexImpl extends FileBasedIndex { } } } - IndexingStamp.flushCache(file); + IndexingStamp.flushCache(inputId); if (unexpectedError != null) { LOG.error(unexpectedError); } @@ -2228,7 +2228,7 @@ public class FileBasedIndexImpl extends FileBasedIndex { try { if (isTooLarge(file)) { List> nontrivialFileIndexedStates = IndexingStamp.getNontrivialFileIndexedStates(fileId); - removeFileDataFromIndices(ContainerUtil.intersection(nontrivialFileIndexedStates, myRequiringContentIndices), file); + removeFileDataFromIndices(ContainerUtil.intersection(nontrivialFileIndexedStates, myRequiringContentIndices), Math.abs(fileId)); } else { doIndexFileContent(project, fileContent); @@ -2250,10 +2250,7 @@ public class FileBasedIndexImpl extends FileBasedIndex { myContentlessIndicesUpdateQueue.ensureUpToDate(); for (VFileEvent event : events) { - Object requestor = event.getRequestor(); - if (requestor instanceof FileDocumentManager || - requestor instanceof PsiManager || - requestor == LocalHistory.VFS_EVENT_REQUESTOR) { + if (memoryStorageCleaningNeeded(event)) { cleanupMemoryStorage(); break; } @@ -2263,6 +2260,13 @@ public class FileBasedIndexImpl extends FileBasedIndex { } } + private boolean memoryStorageCleaningNeeded(VFileEvent event) { + Object requestor = event.getRequestor(); + return requestor instanceof FileDocumentManager || + requestor instanceof PsiManager || + requestor == LocalHistory.VFS_EVENT_REQUESTOR; + } + @Override public void after(@NotNull List events) { myContentlessIndicesUpdateQueue.ensureUpToDate(); @@ -2377,14 +2381,16 @@ public class FileBasedIndexImpl extends FileBasedIndex { } } FileContent fileContent = null; + int inputId = -1; for (ID indexId : myNotRequiringContentIndices) { if (shouldIndexFile(file, indexId)) { oldStuff = false; try { if (fileContent == null) { fileContent = new FileContentImpl(file); + inputId = Math.abs(getFileId(file)); } - updateSingleIndex(indexId, file, fileContent); + updateSingleIndex(indexId, inputId, fileContent); } catch (StorageException e) { LOG.info(e); From 7313ffded5823e42b1bb3dbf6e7267809c8eeef6 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 1 Jul 2015 15:08:21 +0200 Subject: [PATCH 010/137] dumb-aware GutterIconRenderer (EA-66681 - INRE: FileBasedIndexImpl.handleDumbMode) --- .../editor/markup/GutterIconRenderer.java | 7 +++- .../impl/EditorGutterComponentImpl.java | 39 ++++++++++--------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/platform/editor-ui-api/src/com/intellij/openapi/editor/markup/GutterIconRenderer.java b/platform/editor-ui-api/src/com/intellij/openapi/editor/markup/GutterIconRenderer.java index b4f3dcbc30e8..ad865eae4bed 100644 --- a/platform/editor-ui-api/src/com/intellij/openapi/editor/markup/GutterIconRenderer.java +++ b/platform/editor-ui-api/src/com/intellij/openapi/editor/markup/GutterIconRenderer.java @@ -26,10 +26,13 @@ import javax.swing.*; /** * Interface which should be implemented in order to draw icons in the gutter area and handle events * for them. Gutter icons are drawn to the left of the folding area and can be used, for example, - * to mark implemented or overridden methods. + * to mark implemented or overridden methods.

* * Daemon code analyzer checks newly arrived gutter icon renderer against the old one and if they are equal, does not redraw the icon. - * So it is highly advisable to override hashCode()/equals() methods to avoid icon flickering when old gutter renderer gets replaced with the new. + * So it is highly advisable to override hashCode()/equals() methods to avoid icon flickering when old gutter renderer gets replaced with the new.

+ * + * During indexing, click handlers are only invoked for renderers implementing {@link com.intellij.openapi.project.DumbAware}. + * * @author max * @see RangeHighlighter#setGutterIconRenderer(GutterIconRenderer) */ diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java index 98051d1ef8b2..8cb96e2e8af3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java @@ -45,7 +45,7 @@ import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.editor.markup.*; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.DumbService; -import com.intellij.openapi.project.IndexNotReadyException; +import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.Balloon; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Ref; @@ -1379,31 +1379,32 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse } GutterIconRenderer renderer = getGutterRenderer(e); + final Project project = myEditor.getProject(); + if (project != null && DumbService.isDumb(project) && !DumbService.isDumbAware(renderer)) { + DumbService.getInstance(project).showDumbModeNotification("Navigation is not available during indexing"); + return; + } + AnAction clickAction = null; if (renderer != null && e.getButton() < 4) { clickAction = (InputEvent.BUTTON2_MASK & e.getModifiers()) > 0 ? renderer.getMiddleButtonClickAction() : renderer.getClickAction(); } - try { - if (clickAction != null) { - clickAction.actionPerformed(new AnActionEvent(e, myEditor.getDataContext(), "ICON_NAVIGATION", clickAction.getTemplatePresentation(), - ActionManager.getInstance(), - e.getModifiers())); - e.consume(); - repaint(); - } - else { - ActiveGutterRenderer lineRenderer = getActiveRendererByMouseEvent(e); - if (lineRenderer != null) { - lineRenderer.doAction(myEditor, e); - } else { - fireEventToTextAnnotationListeners(e); - } - } + if (clickAction != null) { + clickAction.actionPerformed(new AnActionEvent(e, myEditor.getDataContext(), "ICON_NAVIGATION", clickAction.getTemplatePresentation(), + ActionManager.getInstance(), + e.getModifiers())); + e.consume(); + repaint(); } - catch (IndexNotReadyException e1) { - DumbService.getInstance(myEditor.getProject()).showDumbModeNotification("Navigation is not available during indexing"); + else { + ActiveGutterRenderer lineRenderer = getActiveRendererByMouseEvent(e); + if (lineRenderer != null) { + lineRenderer.doAction(myEditor, e); + } else { + fireEventToTextAnnotationListeners(e); + } } } From 40183cb6d523bf0ce87db78d2ab760a2a0bde5e9 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 1 Jul 2015 15:17:23 +0200 Subject: [PATCH 011/137] wait for smart mode in JavaDirectInheritorsSearcher (EA-70282 - INRE: FileBasedIndexImpl.handleDumbMode) --- .../search/JavaDirectInheritorsSearcher.java | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java index acbf02209732..b24fcfc87c21 100644 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java @@ -101,12 +101,14 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor candidates = ApplicationManager.getApplication().runReadAction(new Computable>() { - @Override - public Collection compute() { - return JavaSuperClassNameOccurenceIndex.getInstance().get(searchKey, project, scope); - } - }); + Collection candidates = MethodUsagesSearcher.resolveInReadAction(project, + new Computable>() { + @Override + public Collection compute() { + return JavaSuperClassNameOccurenceIndex + .getInstance().get(searchKey, project, scope); + } + }); Map> classes = new HashMap>(); @@ -118,7 +120,7 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor() { @Override @@ -142,16 +144,19 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor anonymousCandidates = ApplicationManager.getApplication().runReadAction(new Computable>() { - @Override - public Collection compute() { - return JavaAnonymousClassBaseRefOccurenceIndex.getInstance().get(searchKey, project, scope); - } - }); + Collection anonymousCandidates = MethodUsagesSearcher.resolveInReadAction(project, + new Computable>() { + @Override + public Collection compute() { + return JavaAnonymousClassBaseRefOccurenceIndex + .getInstance() + .get(searchKey, project, scope); + } + }); for (PsiAnonymousClass candidate : anonymousCandidates) { ProgressIndicatorProvider.checkCanceled(); - if (!checkInheritance(p, aClass, candidate)) continue; + if (!checkInheritance(p, aClass, candidate, project)) continue; if (!consumer.process(candidate)) return false; } @@ -190,8 +195,8 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor() { + private static boolean checkInheritance(final DirectClassInheritorsSearch.SearchParameters p, final PsiClass aClass, final PsiClass candidate, Project project) { + return MethodUsagesSearcher.resolveInReadAction(project, new Computable() { @Override public Boolean compute() { return !p.isCheckInheritance() || candidate.isInheritor(aClass, false); From ec39d130a41e4222d262ecbd9ff1b6bb9b2e834b Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 1 Jul 2015 15:19:34 +0200 Subject: [PATCH 012/137] disable usage view action button in dumb mode (EA-70280 - INRE: FileBasedIndexImpl.handleDumbMode) --- .../usageView/src/com/intellij/usages/impl/UsageViewImpl.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java b/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java index 82e9bf9e1bcc..7b4ac5dbee00 100644 --- a/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java +++ b/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java @@ -31,6 +31,7 @@ import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.progress.util.ProgressWrapper; import com.intellij.openapi.progress.util.TooManyUsagesStatus; +import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; @@ -1612,7 +1613,8 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra final JButton button = new JButton(UIUtil.replaceMnemonicAmpersand(text)); DialogUtil.registerMnemonic(button); - + DumbService.getInstance(myProject).makeDumbAware(button, UsageViewImpl.this); + button.setFocusable(false); button.addActionListener(new ActionListener() { @Override From c4a7f15102dd01e9167fee7e860094be03336e3d Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 1 Jul 2015 15:51:31 +0200 Subject: [PATCH 013/137] usage view: dumber settings action name in dumb mode (EA-70208 - INRE: FileBasedIndexImpl.handleDumbMode) --- .../src/com/intellij/usages/impl/UsageViewImpl.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java b/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java index 7b4ac5dbee00..a6da1b610072 100644 --- a/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java +++ b/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java @@ -655,7 +655,15 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra @NotNull private AnAction showSettings() { final ConfigurableUsageTarget configurableUsageTarget = getConfigurableTarget(myTargets); - String description = configurableUsageTarget == null ? "Show find usages settings dialog" : "Show settings for "+configurableUsageTarget.getLongDescriptiveName(); + String description = null; + try { + description = configurableUsageTarget == null ? null : "Show settings for "+configurableUsageTarget.getLongDescriptiveName(); + } + catch (IndexNotReadyException ignored) { + } + if (description == null) { + description = "Show find usages settings dialog"; + } return new AnAction("Settings...", description, AllIcons.General.ProjectSettings) { { KeyboardShortcut shortcut = configurableUsageTarget == null ? getShowUsagesWithSettingsShortcut() : configurableUsageTarget.getShortcut(); From b41d037b09ea149c64d09d7747a5551afe80c00a Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 1 Jul 2015 15:56:45 +0200 Subject: [PATCH 014/137] no recent usages in dumb mode (EA-70195 - INRE: FileBasedIndexImpl.handleDumbMode) --- .../src/com/intellij/find/impl/ShowRecentFindUsagesGroup.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/find/impl/ShowRecentFindUsagesGroup.java b/platform/lang-impl/src/com/intellij/find/impl/ShowRecentFindUsagesGroup.java index ccbb9962831c..c030e9281fab 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/ShowRecentFindUsagesGroup.java +++ b/platform/lang-impl/src/com/intellij/find/impl/ShowRecentFindUsagesGroup.java @@ -19,6 +19,7 @@ package com.intellij.find.impl; import com.intellij.find.FindManager; import com.intellij.find.findUsages.FindUsagesManager; import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.usages.ConfigurableUsageTarget; import com.intellij.usages.impl.UsageViewImpl; @@ -45,7 +46,7 @@ public class ShowRecentFindUsagesGroup extends ActionGroup { public AnAction[] getChildren(@Nullable final AnActionEvent e) { if (e == null) return EMPTY_ARRAY; Project project = e.getData(CommonDataKeys.PROJECT); - if (project == null) return EMPTY_ARRAY; + if (project == null || DumbService.isDumb(project)) return EMPTY_ARRAY; final FindUsagesManager findUsagesManager = ((FindManagerImpl)FindManager.getInstance(project)).getFindUsagesManager(); List history = new ArrayList(findUsagesManager.getHistory().getAll()); Collections.reverse(history); From 2911bd5d1e18545fa7d37538e2fc97769525cb1c Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 1 Jul 2015 16:03:59 +0200 Subject: [PATCH 015/137] make GenerateToStringAction non-dumb-aware (EA-70179 - INRE: FileBasedIndexImpl.handleDumbMode) --- .../java/generate/GenerateToStringAction.java | 12 +++------ .../GenerateToStringActionHandlerImpl.java | 25 ++++++++----------- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/plugins/generate-tostring/src/org/jetbrains/java/generate/GenerateToStringAction.java b/plugins/generate-tostring/src/org/jetbrains/java/generate/GenerateToStringAction.java index c46b70b1d4cc..987a854dee42 100644 --- a/plugins/generate-tostring/src/org/jetbrains/java/generate/GenerateToStringAction.java +++ b/plugins/generate-tostring/src/org/jetbrains/java/generate/GenerateToStringAction.java @@ -15,20 +15,16 @@ */ package org.jetbrains.java.generate; -import com.intellij.openapi.editor.actionSystem.EditorAction; +import com.intellij.codeInsight.generation.actions.BaseGenerateAction; /** - * The IDEA action for this plugin. - *

* This action handles the generation of a toString() method that dumps the fields * of the class. */ -public class GenerateToStringAction extends EditorAction { +public class GenerateToStringAction extends BaseGenerateAction { - /** - * Constructor. - */ public GenerateToStringAction() { - super(new GenerateToStringActionHandlerImpl()); // register our action handler + super(new GenerateToStringActionHandlerImpl()); } + } \ No newline at end of file diff --git a/plugins/generate-tostring/src/org/jetbrains/java/generate/GenerateToStringActionHandlerImpl.java b/plugins/generate-tostring/src/org/jetbrains/java/generate/GenerateToStringActionHandlerImpl.java index 38631f596d73..e574c9278598 100644 --- a/plugins/generate-tostring/src/org/jetbrains/java/generate/GenerateToStringActionHandlerImpl.java +++ b/plugins/generate-tostring/src/org/jetbrains/java/generate/GenerateToStringActionHandlerImpl.java @@ -15,16 +15,14 @@ */ package org.jetbrains.java.generate; +import com.intellij.codeInsight.CodeInsightActionHandler; import com.intellij.codeInsight.generation.PsiElementClassMember; import com.intellij.codeInsight.hint.HintManager; import com.intellij.ide.util.MemberChooser; import com.intellij.openapi.Disposable; -import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.ShowSettingsUtil; @@ -56,14 +54,17 @@ import java.util.List; /** * The action-handler that does the code generation. */ -public class GenerateToStringActionHandlerImpl extends EditorWriteActionHandler implements GenerateToStringActionHandler { +public class GenerateToStringActionHandlerImpl implements GenerateToStringActionHandler, CodeInsightActionHandler { private static final Logger logger = Logger.getInstance("#GenerateToStringActionHandlerImpl"); - public void executeWriteAction(Editor editor, DataContext dataContext) { - final Project project = CommonDataKeys.PROJECT.getData(dataContext); - assert project != null; + @Override + public boolean startInWriteAction() { + return true; + } - PsiClass clazz = getSubjectClass(editor, dataContext); + @Override + public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { + PsiClass clazz = getSubjectClass(editor, file); assert clazz != null; doExecuteAction(project, clazz, editor); @@ -145,14 +146,8 @@ public class GenerateToStringActionHandlerImpl extends EditorWriteActionHandler return GenerationUtil.combineToClassMemberList(filteredFields, filteredMethods); } - @Override - public boolean isEnabled(Editor editor, DataContext dataContext) { - return getSubjectClass(editor, dataContext) != null; - } - @Nullable - private static PsiClass getSubjectClass(Editor editor, DataContext dataContext) { - PsiFile file = CommonDataKeys.PSI_FILE.getData(dataContext); + private static PsiClass getSubjectClass(Editor editor, final PsiFile file) { if (file == null) return null; int offset = editor.getCaretModel().getOffset(); From 9099828c7045b1bde169d6cb2c1461cd6cd903ab Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 1 Jul 2015 16:21:38 +0200 Subject: [PATCH 016/137] maven: invoke importers in smart mode (EA-63588 - INRE: FileBasedIndexImpl.handleDumbMode) --- .../maven/importing/MavenModuleImporter.java | 47 +++++++++++-------- .../jetbrains/idea/maven/utils/MavenUtil.java | 23 +++++++++ 2 files changed, 50 insertions(+), 20 deletions(-) diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenModuleImporter.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenModuleImporter.java index a10ef9750eea..cb7396dfb85b 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenModuleImporter.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenModuleImporter.java @@ -18,6 +18,8 @@ package org.jetbrains.idea.maven.importing; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.intellij.openapi.application.AccessToken; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleType; @@ -121,33 +123,38 @@ public class MavenModuleImporter { } public void configFacets(final List postTasks) { - MavenUtil.invokeAndWaitWriteAction(myModule.getProject(), new Runnable() { + MavenUtil.smartInvokeAndWait(myModule.getProject(), ModalityState.defaultModalityState(), new Runnable() { public void run() { if (myModule.isDisposed()) return; final ModuleType moduleType = ModuleType.get(myModule); - for (final MavenImporter importer : getSuitableImporters()) { - final MavenProjectChanges changes; - if (myMavenProjectChanges == null) { - if (importer.processChangedModulesOnly()) continue; - changes = MavenProjectChanges.NONE; - } - else { - changes = myMavenProjectChanges; - } + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + for (final MavenImporter importer : getSuitableImporters()) { + final MavenProjectChanges changes; + if (myMavenProjectChanges == null) { + if (importer.processChangedModulesOnly()) continue; + changes = MavenProjectChanges.NONE; + } + else { + changes = myMavenProjectChanges; + } - if (importer.getModuleType() == moduleType) { - importer.process(myModifiableModelsProvider, - myModule, - myRootModelAdapter, - myMavenTree, - myMavenProject, - changes, - myMavenProjectToModuleName, - postTasks); + if (importer.getModuleType() == moduleType) { + importer.process(myModifiableModelsProvider, + myModule, + myRootModelAdapter, + myMavenTree, + myMavenProject, + changes, + myMavenProjectToModuleName, + postTasks); + } + } } - } + }); } }); } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java index 47b0e2ec36c4..d92fc84b6731 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java @@ -59,6 +59,7 @@ import com.intellij.psi.PsiManager; import com.intellij.util.DisposeAwareRunnable; import com.intellij.util.Function; import com.intellij.util.SystemProperties; +import com.intellij.util.concurrency.Semaphore; import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashSet; import icons.MavenIcons; @@ -162,6 +163,28 @@ public class MavenUtil { } } } + + public static void smartInvokeAndWait(final Project p, final ModalityState state, final Runnable r) { + if (isNoBackgroundMode() || ApplicationManager.getApplication().isDispatchThread()) { + r.run(); + } + else { + final Semaphore semaphore = new Semaphore(); + semaphore.down(); + DumbService.getInstance(p).smartInvokeLater(new Runnable() { + @Override + public void run() { + try { + r.run(); + } + finally { + semaphore.up(); + } + } + }, state); + semaphore.waitFor(); + } + } public static void invokeAndWaitWriteAction(Project p, final Runnable r) { invokeAndWait(p, new Runnable() { From 40b275a4dd9b30f9a604ba118f532c079c7621d6 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 1 Jul 2015 16:24:52 +0200 Subject: [PATCH 017/137] wait until smart mode before processing file roots while searching for usages (EA-65020 - INRE: FileBasedIndexImpl.handleDumbMode) --- .../com/intellij/psi/impl/search/PsiSearchHelperImpl.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java b/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java index 704aae2194bb..0969093acc78 100644 --- a/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java +++ b/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java @@ -312,7 +312,7 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { } // we failed to run read action in job launcher thread // run read action in our thread instead to wait for a write action to complete and resume parallel processing - ApplicationManager.getApplication().runReadAction(EmptyRunnable.getInstance()); + DumbService.getInstance(myManager.getProject()).runReadActionInSmartMode(EmptyRunnable.getInstance()); files = failedList; } return completed; @@ -347,7 +347,10 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { ApplicationUtil.tryRunReadAction(new Computable() { @Override public Void compute() { - if (myManager.getProject().isDisposed()) throw new ProcessCanceledException(); + final Project project = myManager.getProject(); + if (project.isDisposed()) throw new ProcessCanceledException(); + if (DumbService.isDumb(project)) throw new ApplicationUtil.CannotRunReadActionException(); + List psiRoots = file.getViewProvider().getAllFiles(); Set processed = new THashSet(psiRoots.size() * 2, (float)0.5); for (final PsiFile psiRoot : psiRoots) { From de79143688a8bb5ae2413dab6970aacefc157c8b Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 1 Jul 2015 16:28:53 +0200 Subject: [PATCH 018/137] disable PyMethodNameTypedHandler in dumb mode (EA-70199 - INRE: FileBasedIndexImpl.handleDumbMode) --- .../jetbrains/python/codeInsight/PyMethodNameTypedHandler.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/codeInsight/PyMethodNameTypedHandler.java b/python/src/com/jetbrains/python/codeInsight/PyMethodNameTypedHandler.java index dd9d3ef9dc00..d2b01b388544 100644 --- a/python/src/com/jetbrains/python/codeInsight/PyMethodNameTypedHandler.java +++ b/python/src/com/jetbrains/python/codeInsight/PyMethodNameTypedHandler.java @@ -21,6 +21,7 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorModificationUtil; import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; @@ -40,7 +41,7 @@ import com.jetbrains.python.psi.PyUtil; public class PyMethodNameTypedHandler extends TypedHandlerDelegate { @Override public Result beforeCharTyped(char character, Project project, Editor editor, PsiFile file, FileType fileType) { - if (!(fileType instanceof PythonFileType)) return Result.CONTINUE; // else we'd mess up with other file types! + if (DumbService.isDumb(project) || !(fileType instanceof PythonFileType)) return Result.CONTINUE; // else we'd mess up with other file types! if (character == '(') { if (!PyCodeInsightSettings.getInstance().INSERT_SELF_FOR_METHODS) { return Result.CONTINUE; From 324e0d924b896924532339b9fb7f0d45075cc3fd Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Tue, 30 Jun 2015 17:48:00 +0300 Subject: [PATCH 019/137] IDEA-79921 Suspend one thread while debugging --- .../src/com/intellij/debugger/actions/ResumeThreadAction.java | 1 - .../src/com/intellij/debugger/engine/DebugProcessImpl.java | 1 + .../impl/src/com/intellij/debugger/impl/DebuggerSession.java | 4 +++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ResumeThreadAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/ResumeThreadAction.java index 709383481549..4c9e57aa13c7 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/ResumeThreadAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/ResumeThreadAction.java @@ -45,7 +45,6 @@ public class ResumeThreadAction extends DebuggerAction{ final ThreadReferenceProxyImpl thread = threadDescriptor.getThreadReference(); debugProcess.getManagerThread().schedule(new SuspendContextCommandImpl(debuggerContext.getSuspendContext()) { public void contextAction() throws Exception { - debugProcess.getSession().getXDebugSession().sessionResumed(); debugProcess.createResumeThreadCommand(getSuspendContext(), thread).run(); debuggerTreeNode.calcValue(); } 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 43149c603d8f..fa3cfe540b65 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java @@ -1694,6 +1694,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb final Set suspendingContexts = SuspendManagerUtil.getSuspendingContexts(getSuspendManager(), myThread); for (SuspendContextImpl suspendContext : suspendingContexts) { if (suspendContext.getThread() == myThread) { + getSession().getXDebugSession().sessionResumed(); getManagerThread().invoke(createResumeCommand(suspendContext)); } else { diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerSession.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerSession.java index 04bf6d8aa302..42d19808729e 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerSession.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerSession.java @@ -589,7 +589,9 @@ public class DebuggerSession implements AbstractDebuggerSession { @Override public void resumed(final SuspendContextImpl suspendContext) { - final SuspendContextImpl currentContext = isSteppingThrough(suspendContext.getThread()) ? null : getProcess().getSuspendManager().getPausedContext(); + final SuspendContextImpl currentContext = suspendContext != null && isSteppingThrough(suspendContext.getThread()) + ? null + : getProcess().getSuspendManager().getPausedContext(); DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() { @Override public void run() { From 45715dab3cbf198f82c897946d9bf5ac77fa242f Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Wed, 1 Jul 2015 17:49:43 +0300 Subject: [PATCH 020/137] fixed breakpoints in anonymous classes in decompiled code --- .../debugger/engine/PositionManagerImpl.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java index 77b1c44f093d..6ec3eefddb17 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java @@ -28,16 +28,14 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.NullableComputable; -import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.*; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.search.FilenameIndex; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.DocumentUtil; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.EmptyIterable; @@ -285,13 +283,12 @@ public class PositionManagerImpl implements PositionManager, MultiRequestPositio if (document == null || lineNumber >= document.getLineCount()) { return EmptyIterable.getInstance(); } - final int startOffset = document.getLineStartOffset(lineNumber); - final int endOffset = document.getLineEndOffset(lineNumber); + final TextRange lineRange = DocumentUtil.getLineTextRange(document, lineNumber); return new Iterable() { @Override public Iterator iterator() { return new Iterator() { - PsiElement myElement = file.findElementAt(startOffset); + PsiElement myElement = DebuggerUtilsEx.findElementAt(file, lineRange.getStartOffset()); @Override public boolean hasNext() { @@ -303,7 +300,7 @@ public class PositionManagerImpl implements PositionManager, MultiRequestPositio PsiElement res = myElement; do { myElement = PsiTreeUtil.nextLeaf(myElement); - if (myElement == null || myElement.getTextOffset() > endOffset) { + if (myElement == null || myElement.getTextOffset() > lineRange.getEndOffset()) { myElement = null; break; } From 4885c0aebbece72e1ca4d6e3c753eb08af4518c7 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Wed, 1 Jul 2015 18:02:15 +0300 Subject: [PATCH 021/137] IDEA-140989 Cannot select item from completion popup using mouse --- .../src/com/intellij/ui/BalloonImpl.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java index 8c593a69d52c..d60649e096a0 100644 --- a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java @@ -106,7 +106,7 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { final boolean insideBalloon = isInsideBalloon(me); if (myHideOnMouse && id == MouseEvent.MOUSE_PRESSED) { - if (!insideBalloon && !hasModalDialog(me)) { + if (!insideBalloon && !hasModalDialog(me) && !isWithinChildWindow(me)) { hide(); } return; @@ -159,6 +159,21 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { } }; + private boolean isWithinChildWindow(MouseEvent event) { + Component owner = UIUtil.getWindow(myContent); + if (owner != null) { + Component child = UIUtil.getWindow(event.getComponent()); + if (child != owner) { + for (; child != null; child = child.getParent()) { + if (child == owner) { + return true; + } + } + } + } + return false; + } + private static boolean hasModalDialog(MouseEvent e) { final Component c = e.getComponent(); final DialogWrapper dialog = DialogWrapper.findInstance(c); From fdc45ffe96a6112dde9dedb60f9e4753fc2d8401 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Wed, 1 Jul 2015 18:27:22 +0300 Subject: [PATCH 022/137] vcs: fix double click listener in ChangesBrowser * check for checkbox area only if checkboxes are shown --- .../vcs/changes/ui/ChangesTreeList.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTreeList.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTreeList.java index a94a1af63689..05fab9131753 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTreeList.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTreeList.java @@ -203,13 +203,15 @@ public abstract class ChangesTreeList extends JPanel implements TypeSafeDataP public boolean onClick(@NotNull MouseEvent e, int clickCount) { final int idx = myList.locationToIndex(e.getPoint()); if (idx >= 0) { - final Rectangle baseRect = myList.getCellBounds(idx, idx); - baseRect.setSize(checkboxWidth, baseRect.height); - if (baseRect.contains(e.getPoint())) { - toggleSelection(); - return true; + if (myShowCheckboxes) { + final Rectangle baseRect = myList.getCellBounds(idx, idx); + baseRect.setSize(checkboxWidth, baseRect.height); + if (baseRect.contains(e.getPoint())) { + toggleSelection(); + return true; + } } - else if (clickCount == 2) { + if (clickCount == 2) { myDoubleClickHandler.run(); return true; } @@ -228,9 +230,11 @@ public abstract class ChangesTreeList extends JPanel implements TypeSafeDataP final int row = myTree.getRowForLocation(e.getPoint().x, e.getPoint().y); if (row >= 0) { - final Rectangle baseRect = myTree.getRowBounds(row); - baseRect.setSize(checkboxWidth, baseRect.height); - if (baseRect.contains(e.getPoint())) return false; + if (myShowCheckboxes) { + final Rectangle baseRect = myTree.getRowBounds(row); + baseRect.setSize(checkboxWidth, baseRect.height); + if (baseRect.contains(e.getPoint())) return false; + } } myDoubleClickHandler.run(); From ecb0d36fde261037619f031a03237b1f37ec767f Mon Sep 17 00:00:00 2001 From: "Vassiliy.Kudryashov" Date: Wed, 1 Jul 2015 19:00:25 +0300 Subject: [PATCH 023/137] cleanup --- .../com/intellij/execution/impl/RunConfigurableTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/java-tests/testSrc/com/intellij/execution/impl/RunConfigurableTest.java b/java/java-tests/testSrc/com/intellij/execution/impl/RunConfigurableTest.java index 629f102de77d..847dc0f64d64 100644 --- a/java/java-tests/testSrc/com/intellij/execution/impl/RunConfigurableTest.java +++ b/java/java-tests/testSrc/com/intellij/execution/impl/RunConfigurableTest.java @@ -131,7 +131,7 @@ public class RunConfigurableTest extends LightIdeaTestCase { private void doExpand() { List toExpand = new ArrayList(); RunConfigurable.collectNodesRecursively(myRoot, toExpand, FOLDER); - assertEquals(toExpand.size(), 5); + assertEquals(5, toExpand.size()); List toExpand2 = new ArrayList(); RunConfigurable.collectNodesRecursively(myRoot, toExpand2, CONFIGURATION_TYPE); toExpand.addAll(toExpand2); From c226409f8afbb1008a3270d1931491a74f2e1121 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Wed, 1 Jul 2015 18:45:37 +0300 Subject: [PATCH 024/137] IDEA-142086 diff: mark files dirty if launched from command line they can be not under FileWatcher, so refresh() will do nothing even if they were changed --- .../com/intellij/diff/applications/DiffApplication.java | 4 ++-- .../src/com/intellij/ide/diff/VirtualFileDiffElement.java | 7 ++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/platform/diff-impl/src/com/intellij/diff/applications/DiffApplication.java b/platform/diff-impl/src/com/intellij/diff/applications/DiffApplication.java index 0bbc064b3cab..9e2f84f0b1ca 100644 --- a/platform/diff-impl/src/com/intellij/diff/applications/DiffApplication.java +++ b/platform/diff-impl/src/com/intellij/diff/applications/DiffApplication.java @@ -23,6 +23,7 @@ import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.diff.DiffBundle; import com.intellij.openapi.project.DefaultProjectFactory; import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -57,8 +58,7 @@ public class DiffApplication extends ApplicationStarterBase { if (file1 == null) throw new Exception("Can't find file " + path1); if (file2 == null) throw new Exception("Can't find file " + path2); - file1.refresh(false, true); - file2.refresh(false, true); + VfsUtil.markDirtyAndRefresh(false, false, false, file1, file2); DiffRequest request = DiffRequestFactory.getInstance().createFromFiles(null, file1, file2); Project project = DefaultProjectFactory.getInstance().getDefaultProject(); diff --git a/platform/platform-impl/src/com/intellij/ide/diff/VirtualFileDiffElement.java b/platform/platform-impl/src/com/intellij/ide/diff/VirtualFileDiffElement.java index b697be14276f..13826a6ae112 100644 --- a/platform/platform-impl/src/com/intellij/ide/diff/VirtualFileDiffElement.java +++ b/platform/platform-impl/src/com/intellij/ide/diff/VirtualFileDiffElement.java @@ -32,10 +32,7 @@ import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.LocalFileSystem; -import com.intellij.openapi.vfs.VFileProperty; -import com.intellij.openapi.vfs.VfsUtilCore; -import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.*; import com.intellij.util.PlatformIcons; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -301,7 +298,7 @@ public class VirtualFileDiffElement extends DiffElement { }.execute(); } - virtualFile.refresh(true, true); + VfsUtil.markDirtyAndRefresh(true, true, true, virtualFile); } } } From 2bc3208360575ac5a5232bec8c10db504668aeb2 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Wed, 1 Jul 2015 18:13:33 +0200 Subject: [PATCH 025/137] ThreeStateCheckBox eats CPU. A lot. --- .../intellij/util/ui/ThreeStateCheckBox.java | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/platform/util/src/com/intellij/util/ui/ThreeStateCheckBox.java b/platform/util/src/com/intellij/util/ui/ThreeStateCheckBox.java index 8f9bbc29bb9d..8f7bd5bbf3b1 100644 --- a/platform/util/src/com/intellij/util/ui/ThreeStateCheckBox.java +++ b/platform/util/src/com/intellij/util/ui/ThreeStateCheckBox.java @@ -63,7 +63,7 @@ public class ThreeStateCheckBox extends JCheckBox { @Override public boolean isSelected() { - return myState == State.SELECTED; + return myState == State.SELECTED || (UIUtil.isUnderAquaLookAndFeel() && myState == State.DONT_CARE); } }); @@ -101,6 +101,10 @@ public class ThreeStateCheckBox extends JCheckBox { public void setState(State state) { myState = state; + + String value = state == State.DONT_CARE ? "indeterminate" : null; + putClientProperty("JButton.selectedState", value); + repaint(); } @@ -111,12 +115,11 @@ public class ThreeStateCheckBox extends JCheckBox { @Override protected void paintComponent(Graphics g) { + super.paintComponent(g); if (UIUtil.isUnderAquaLookAndFeel()) { - paintIndeterminateIcon(g); return; } - super.paintComponent(g); switch (getState()) { case DONT_CARE: Icon icon = getIcon(); @@ -155,19 +158,4 @@ public class ThreeStateCheckBox extends JCheckBox { break; } } - - protected void paintIndeterminateIcon(Graphics g) { - State initial = getState(); - try { - if (getState() == State.DONT_CARE) { - setSelected(true); - putClientProperty("JButton.selectedState", "indeterminate"); - } else { - putClientProperty("JButton.selectedState", null); - } - super.paintComponent(g); - } finally { - setState(initial); - } - } } From 86f213e979c2f15df0354626d930270af9d3288a Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Wed, 1 Jul 2015 19:39:07 +0300 Subject: [PATCH 026/137] NewPropertyAction checks empty property keys --- .../com/intellij/lang/properties/editor/NewPropertyAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/properties/src/com/intellij/lang/properties/editor/NewPropertyAction.java b/plugins/properties/src/com/intellij/lang/properties/editor/NewPropertyAction.java index 4bad9e837f41..0b39ec495548 100644 --- a/plugins/properties/src/com/intellij/lang/properties/editor/NewPropertyAction.java +++ b/plugins/properties/src/com/intellij/lang/properties/editor/NewPropertyAction.java @@ -131,7 +131,7 @@ class NewPropertyAction extends AnAction { @Override public boolean checkInput(final String inputString) { - return true; + return !inputString.isEmpty(); } @Override From 3e32a3979c2bb05bc42d3d55e83d97e4f1777fc9 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Wed, 1 Jul 2015 19:56:18 +0300 Subject: [PATCH 027/137] PseudoFunctionStyleMethodInspection: settings improved to avoid NPE, disable all panels when no templates wo show --- .../java18StreamApi/AddMethodsDialog.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/AddMethodsDialog.java b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/AddMethodsDialog.java index d84d67e879f4..376d3a38b82a 100644 --- a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/AddMethodsDialog.java +++ b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/AddMethodsDialog.java @@ -36,6 +36,7 @@ import com.intellij.ui.ListCellRendererWrapper; import com.intellij.ui.SimpleTextAttributes; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.LinkedMultiMap; import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -151,11 +152,11 @@ public class AddMethodsDialog extends DialogWrapper { final DefaultComboBoxModel comboBoxModel = (DefaultComboBoxModel)myMethodNameCombo.getModel(); comboBoxModel.removeAllElements(); if (aClass == null) { - myMethodNameCombo.setEnabled(false); + enable(false); } else { final List possibleTemplates = PseudoLambdaReplaceTemplate.getAllTemplates(); - final MultiMap nameToMethod = new MultiMap(); + final LinkedMultiMap nameToMethod = new LinkedMultiMap(); for (PsiMethod m : ContainerUtil.filter(aClass.getMethods(), new Condition() { @Override public boolean value(PsiMethod method) { @@ -181,15 +182,27 @@ public class AddMethodsDialog extends DialogWrapper { for (Map.Entry> entry : nameToMethod.entrySet()) { comboBoxModel.addElement(entry.getValue()); } - myMethodNameCombo.setEnabled(true); + final boolean isSuitableMethodsFound = comboBoxModel.getSize() != 0; + enable(isSuitableMethodsFound); } } }); - myExamplePanel.setEnabled(false); + setOKActionEnabled(false); init(); } + private void enable(boolean isEnabled) { + myMethodNameCombo.setEnabled(isEnabled); + myTemplatesCombo.setEnabled(isEnabled); + setOKActionEnabled(isEnabled); + myExamplePanel.setEnabled(isEnabled); + if (!isEnabled) { + myBeforeActionPanel.reset("", StdFileTypes.JAVA); + myAfterActionPanel.reset("", StdFileTypes.JAVA); + } + } + private void showTemplateExample(final PseudoLambdaReplaceTemplate template, final PsiMethod method) { final PsiClass aClass = method.getContainingClass(); LOG.assertTrue(aClass != null); From 5fb0836db1d1214ea88752fe1969c53d5a991112 Mon Sep 17 00:00:00 2001 From: "Vladimir.Orlov" Date: Wed, 1 Jul 2015 19:58:02 +0300 Subject: [PATCH 028/137] fixed exec permission for binaries into unix jre bundled. --- python/edu/build/pycharm_edu_build.gant | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/edu/build/pycharm_edu_build.gant b/python/edu/build/pycharm_edu_build.gant index e1ed6b8f8aaf..b5ae93ba586a 100644 --- a/python/edu/build/pycharm_edu_build.gant +++ b/python/edu/build/pycharm_edu_build.gant @@ -204,7 +204,7 @@ public layoutEducational(String classesPath, Set usedJars) { String tarRoot = isEap() ? "pycharm-edu-$buildNumber" : "pycharm-edu-${p("component.version.major")}.${p("component.version.minor")}" buildTarGz(tarRoot, "$paths.artifacts/pycharm${buildName}.tar", [paths.distAll, paths.distUnix]) if (p("jdk.bundled.linux") != "false") { - buildTarGz(tarRoot, "$paths.artifacts/pycharm${buildName}-jdk-bundled.tar", [paths.distAll, paths.distUnix, "${paths.sandbox}/bundled.linux.jdk"], ["jre/bin/*"]) + buildTarGz(tarRoot, "$paths.artifacts/pycharm${buildName}-jdk-bundled.tar", [paths.distAll, paths.distUnix, "${paths.sandbox}/bundled.linux.jdk"], ["jre/jre/bin/*"]) } From 96628e419e5bd4fdc4f8e5f920c2c20277aebba0 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Wed, 1 Jul 2015 20:45:27 +0300 Subject: [PATCH 029/137] EA-69844 - OCE: DebugProcessImpl.deleteStepRequests --- .../impl/src/com/intellij/debugger/engine/DebugProcessImpl.java | 2 ++ 1 file changed, 2 insertions(+) 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 fa3cfe540b65..9da93a15cda6 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java @@ -456,6 +456,8 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb catch (IllegalThreadStateException e) { LOG.info(e); // undocumented by JDI: may be thrown when querying thread status } + catch (ObjectCollectedException ignored) { + } } requestManager.deleteEventRequests(toDelete); } From 69e60465d56b1dd468deb9c9e89b8c00e9a6e392 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 1 Jul 2015 20:10:53 +0300 Subject: [PATCH 030/137] Extracted several logically independent parts from resolveChild() --- .../python/psi/resolve/ResolveImportUtil.java | 115 +++++++++++------- 1 file changed, 71 insertions(+), 44 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java index b156b608c6bb..5475aa969896 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java +++ b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java @@ -269,56 +269,83 @@ public class ResolveImportUtil { @Nullable public static PsiElement resolveChild(@Nullable final PsiElement parent, @NotNull final String referencedName, @Nullable final PsiFile containingFile, boolean fileOnly, boolean checkForPackage) { - PsiDirectory dir = null; - PsiElement resultElement = null; - final PyResolveContext resolveContext = PyResolveContext.defaultContext(); - if (parent instanceof PyFileImpl) { - PsiElement possibleResult = null; - if (PyNames.INIT_DOT_PY.equals(((PyFile)parent).getName())) { - dir = ((PyFile)parent).getContainingDirectory(); - possibleResult = resolveInDirectory(referencedName, containingFile, dir, fileOnly, checkForPackage); - } - - final PyModuleType moduleType = new PyModuleType((PyFile)parent); - final List results = moduleType.resolveMember(referencedName, null, AccessDirection.READ, - resolveContext); - final PsiElement moduleMember = results != null && !results.isEmpty() ? results.get(0).getElement() : null; - if (!fileOnly || PyUtil.instanceOf(moduleMember, PsiFile.class, PsiDirectory.class)) { - resultElement = moduleMember; - } - if (resultElement != null && !PyUtil.instanceOf(resultElement, PsiFile.class, PsiDirectory.class) && - PsiTreeUtil.getStubOrPsiParentOfType(resultElement, PyExceptPart.class) == null && !isDunderAll(resultElement)) { - return resultElement; - } - - if (possibleResult != null) return possibleResult; - - if (resultElement != null) { - return resultElement; - } + if (parent == null) { + return null; + } + else if (parent instanceof PyFile) { + return resolveInPackageModule((PyFile)parent, referencedName, containingFile, fileOnly, checkForPackage); } else if (parent instanceof PsiDirectory) { - dir = (PsiDirectory)parent; + return resolveInPackageDirectory(parent, referencedName, containingFile, fileOnly, checkForPackage); } - else if (parent != null) { - PyType refType = PyReferenceExpressionImpl.getReferenceTypeFromProviders(parent, resolveContext.getTypeEvalContext(), null); - if (refType != null) { - final List result = refType.resolveMember(referencedName, null, AccessDirection.READ, resolveContext); - if (result != null && !result.isEmpty()) { - return result.get(0).getElement(); - } - } + else { + return resolveMemberFromReferenceTypeProviders(parent, referencedName); } - if (dir != null) { - final PsiElement result = resolveInDirectory(referencedName, containingFile, dir, fileOnly, checkForPackage); - if (result != null) { - return result; + } + + @Nullable + private static PsiElement resolveInPackageModule(@NotNull PyFile parent, @NotNull String referencedName, + @Nullable PsiFile containingFile, boolean fileOnly, boolean checkForPackage) { + final PsiElement moduleMember = resolveModuleMember(parent, referencedName); + final PsiElement resolved = !fileOnly || PyUtil.instanceOf(moduleMember, PsiFile.class, PsiDirectory.class) ? + moduleMember : null; + if (resolved != null && !preferResolveInDirectoryOverModule(resolved)) { + return resolved; + } + + final PsiElement resolvedInDirectory = resolveInPackageDirectory(parent, referencedName, containingFile, fileOnly, checkForPackage); + if (resolvedInDirectory != null) { + return resolvedInDirectory; + } + + return resolved; + } + + private static boolean preferResolveInDirectoryOverModule(@NotNull PsiElement resolved) { + return PsiTreeUtil.getStubOrPsiParentOfType(resolved, PyExceptPart.class) != null || + PyUtil.instanceOf(resolved, PsiFile.class, PsiDirectory.class) || // XXX: Workaround for PY-9439 + isDunderAll(resolved); + } + + @Nullable + private static PsiElement resolveModuleMember(@NotNull PyFile file, @NotNull String referencedName) { + final PyModuleType moduleType = new PyModuleType(file); + final PyResolveContext resolveContext = PyResolveContext.defaultContext(); + final List results = moduleType.resolveMember(referencedName, null, AccessDirection.READ, + resolveContext); + return results != null && !results.isEmpty() ? results.get(0).getElement() : null; + } + + @Nullable + private static PsiElement resolveInPackageDirectory(@Nullable PsiElement parent, @NotNull String referencedName, + @Nullable PsiFile containingFile, boolean fileOnly, + boolean checkForPackage) { + final PsiElement parentDir = PyUtil.turnInitIntoDir(parent); + if (parentDir instanceof PsiDirectory) { + final PsiElement resolved = resolveInDirectory(referencedName, containingFile, (PsiDirectory)parentDir, fileOnly, checkForPackage); + if (resolved != null) { + return resolved; } if (parent instanceof PsiFile) { - final PsiElement element = new QualifiedNameResolverImpl(referencedName).fromElement(parent).withoutRoots().firstResult(); - if (element != null) { - return element; - } + return resolveForeignImports((PsiFile)parent, referencedName); + } + } + return null; + } + + @Nullable + private static PsiElement resolveForeignImports(@NotNull PsiFile foothold, @NotNull String referencedName) { + return new QualifiedNameResolverImpl(referencedName).fromElement(foothold).withoutRoots().firstResult(); + } + + @Nullable + private static PsiElement resolveMemberFromReferenceTypeProviders(@NotNull PsiElement parent, @NotNull String referencedName) { + final PyResolveContext resolveContext = PyResolveContext.defaultContext(); + PyType refType = PyReferenceExpressionImpl.getReferenceTypeFromProviders(parent, resolveContext.getTypeEvalContext(), null); + if (refType != null) { + final List result = refType.resolveMember(referencedName, null, AccessDirection.READ, resolveContext); + if (result != null && !result.isEmpty()) { + return result.get(0).getElement(); } } return null; From b063f4d30fe65c30090bd97384fbfa6c2a5c9586 Mon Sep 17 00:00:00 2001 From: Andrey Sokolov Date: Wed, 1 Jul 2015 20:52:21 +0300 Subject: [PATCH 031/137] StringBasedPostfixTemplate api - added new method getElementToRemove --- .../postfix/templates/StringBasedPostfixTemplate.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/StringBasedPostfixTemplate.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/StringBasedPostfixTemplate.java index 85e71d73b247..0f7b93ef98ea 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/StringBasedPostfixTemplate.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/StringBasedPostfixTemplate.java @@ -37,7 +37,7 @@ public abstract class StringBasedPostfixTemplate extends PostfixTemplateWithExpr public final void expandForChooseExpression(@NotNull PsiElement expr, @NotNull Editor editor) { Project project = expr.getProject(); Document document = editor.getDocument(); - PsiElement elementForRemoving = shouldRemoveParent() ? expr.getParent() : expr; + PsiElement elementForRemoving = getElementToRemove(expr); document.deleteString(elementForRemoving.getTextRange().getStartOffset(), elementForRemoving.getTextRange().getEndOffset()); TemplateManager manager = TemplateManager.getInstance(project); @@ -78,7 +78,16 @@ public abstract class StringBasedPostfixTemplate extends PostfixTemplateWithExpr return true; } + /** @deprecated use {@link StringBasedPostfixTemplate#getElementToRemove(PsiElement)} (idea 16 to remove) */ protected boolean shouldRemoveParent() { return true; } + + protected PsiElement getElementToRemove(PsiElement expr) { + if (shouldRemoveParent()) { + return expr.getParent(); + } else { + return expr; + } + } } From 2a063c19d422b99424a652e78f08d8f8b01c7a51 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 1 Jul 2015 21:03:30 +0300 Subject: [PATCH 032/137] Cleanup (formatting) --- .../parser/partial/DeclarationParserTest.java | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java index 960417707dda..4f0ecb3d6e7a 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java @@ -34,19 +34,12 @@ public class DeclarationParserTest extends JavaParsingTestCase { public void testPines() { doParserTest("{ class A> extends List> { } }"); } public void testIncompleteAnnotation() { doParserTest("{ public class Foo { public void testSomething(); @Null } }"); } public void testClassInit() { doParserTest("{ { /*comment*/ } }"); } - public void testAnnoDeclaration() { doParserTest("{ public @interface Annotation {} }"); } - public void testEnumSmartTypeCompletion() { doParserTest("{ @Preliminary(A.B\n#) public class TimeTravel {}\n" + - " @Preliminary(a=A.B\n#) public class TimeTravel {}\n" + - " @Preliminary(a=A.B\n#, b=c) public class TimeTravel {} }"); } - public void testTypeAnno() { - doParserTest("{ class C<@D T extends @F Object> extends @F Object {\n" + - " @F int @F[] method() throws @F Exception {\n" + - " a = this instanceof @F C;\n" + - " C<@F @G C> c = new @Q C<@F C>();\n" + - " c = (@F Object)c;\n" + - " Class c = @TA String.class;\n" + - " @F C.field++;\n" + - " }\n} }"); + + public void testEnumSmartTypeCompletion() { + doParserTest( + "{ @Preliminary(A.B\n#) public class TimeTravel {}\n" + + " @Preliminary(a=A.B\n#) public class TimeTravel {}\n" + + " @Preliminary(a=A.B\n#, b=c) public class TimeTravel {} }"); } public void testEnumBody0() { doParserTest("{ ; }", false, true); } @@ -59,6 +52,7 @@ public class DeclarationParserTest extends JavaParsingTestCase { public void testEnumWithInitializedConstants() { doParserTest("{ A(10) { },\n B { void method() {} } }", false, true); } public void testEnumWithoutConstants() { doParserTest("{ private A }", false, true); } + public void testAnnoDeclaration() { doParserTest("{ public @interface Annotation {} }"); } public void testAnnoSimple() { doParserTest("{ int foo (); }", true, false); } public void testAnnoDefault() { doParserTest("{ Class foo() default String.class; }", true, false); } public void testAnnoNested() { doParserTest("{ @interface Inner { String bar () default \"\"; } }", true, false); } @@ -66,6 +60,18 @@ public class DeclarationParserTest extends JavaParsingTestCase { public void testAnnoOtherMembers() { doParserTest("{ int field;\n void m() {}\n class C {}\n interface I {} }", true, false); } public void testAnnoLoop() { doParserTest("{ @@@ int i; }"); } + public void testTypeAnno() { + doParserTest( + "{ class C<@D T extends @F Object> extends @F Object {\n" + + " @F int @F[] method() throws @F Exception {\n" + + " a = this instanceof @F C;\n" + + " C<@F @G C> c = new @Q C<@F C>();\n" + + " c = (@F Object)c;\n" + + " Class c = @TA String.class;\n" + + " @F C.field++;\n" + + " }\n} }"); + } + public void testFieldSimple() { doParserTest("{ int field = 0; }"); } public void testFieldMulti() { doParserTest("{ int field1 = 0, field2; }"); } public void testUnclosedBracket() { doParserTest("{ int field[ }"); } @@ -94,9 +100,13 @@ public class DeclarationParserTest extends JavaParsingTestCase { public void testConstructorBrackets() { doParserTest("{ A() [] { } }"); } public void testVarArgBrackets() { doParserTest("{ void foo(int... x[]); }"); } - public void testGenericMethod() { doParserTest("{ public static test();\n" + - " void test1();\n" + - " String test2(); }"); } + public void testGenericMethod() { + doParserTest( + "{ public static test();\n" + + " void test1();\n" + + " String test2(); }"); + } + public void testGenericMethodErrors() { doParserTest("{ test (); }"); } public void testErrors() { doParserTest("{ public static protected int f1 = 0; }"); } public void testCompletionHack0() { doParserTest("{ \n String s = \"\"; }"); } From afbe2e071748bd94d55429c6a7bd47d4499f184f Mon Sep 17 00:00:00 2001 From: Andrey Sokolov Date: Wed, 1 Jul 2015 21:27:21 +0300 Subject: [PATCH 033/137] FormatPostfixTemplate replace deprecated usage of method shouldRemoveParent with usage of getElementToRemove --- .../template/postfix/templates/FormatPostfixTemplate.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/FormatPostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/FormatPostfixTemplate.java index 5b88af016709..9d90c20d2e5b 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/FormatPostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/FormatPostfixTemplate.java @@ -50,7 +50,7 @@ public class FormatPostfixTemplate extends StringBasedPostfixTemplate { } @Override - protected boolean shouldRemoveParent() { - return false; + protected PsiElement getElementToRemove(PsiElement expr) { + return expr; } } From 1d09eff133e9e347580a235392514c54250ddf5b Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Wed, 1 Jul 2015 18:51:28 +0300 Subject: [PATCH 034/137] mark tests progress grey if terminated and was green (IDEA-142074) --- .../testframework/sm/runner/ui/SMTestRunnerResultsForm.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/SMTestRunnerResultsForm.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/SMTestRunnerResultsForm.java index 24ba21837c54..99f70a67f5c6 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/SMTestRunnerResultsForm.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/SMTestRunnerResultsForm.java @@ -319,6 +319,10 @@ public class SMTestRunnerResultsForm extends TestResultsPanel } fireOnTestingFinished(); + + if (testsRoot.wasTerminated() && myStatusLine.getStatusColor() == ColorProgressBar.GREEN) { + myStatusLine.setStatusColor(JBColor.LIGHT_GRAY); + } if (testsRoot.isEmptySuite() && testsRoot.isTestsReporterAttached() && From 01d259f8cccff87078679ca4bcf34631a6a4490d Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Wed, 1 Jul 2015 19:21:40 +0300 Subject: [PATCH 035/137] import tests: ensure additional actions are included into imported view; runner is chosen according to initial configuration (IDEA-142115) --- .../history/ImportedTestConsoleProperties.java | 10 ++++++++++ .../actions/AbstractImportTestsAction.java | 15 ++++++++++++++- .../testframework/TestConsoleProperties.java | 2 +- .../ui/properties/JUnitConsoleProperties.java | 4 ++-- .../testng/model/TestNGConsoleProperties.java | 2 +- 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/history/ImportedTestConsoleProperties.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/history/ImportedTestConsoleProperties.java index 0b529b46083d..4241ba73fa57 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/history/ImportedTestConsoleProperties.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/history/ImportedTestConsoleProperties.java @@ -28,11 +28,14 @@ import com.intellij.execution.testframework.sm.runner.SMTRunnerConsoleProperties import com.intellij.execution.testframework.sm.runner.SMTestLocator; import com.intellij.execution.testframework.sm.runner.TestProxyFilterProvider; import com.intellij.execution.ui.ConsoleView; +import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.project.Project; import com.intellij.pom.Navigatable; +import com.intellij.util.config.ToggleBooleanProperty; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import javax.swing.*; import java.io.File; public class ImportedTestConsoleProperties extends SMTRunnerConsoleProperties implements SMCustomMessagesParsing { @@ -111,4 +114,11 @@ public class ImportedTestConsoleProperties extends SMTRunnerConsoleProperties im public AbstractRerunFailedTestsAction createRerunFailedTestsAction(ConsoleView consoleView) { return myProperties == null ? null : myProperties.createRerunFailedTestsAction(consoleView); } + + @Override + public void appendAdditionalActions(DefaultActionGroup actionGroup, JComponent parent) { + if (myProperties != null) { + myProperties.appendAdditionalActions(actionGroup, parent); + } + } } diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/history/actions/AbstractImportTestsAction.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/history/actions/AbstractImportTestsAction.java index cab2b7f18147..efd1d7342f62 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/history/actions/AbstractImportTestsAction.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/history/actions/AbstractImportTestsAction.java @@ -18,6 +18,7 @@ package com.intellij.execution.testframework.sm.runner.history.actions; import com.intellij.execution.ExecutionException; import com.intellij.execution.Executor; import com.intellij.execution.ExecutorRegistry; +import com.intellij.execution.RunnerRegistry; import com.intellij.execution.configurations.RunConfiguration; import com.intellij.execution.configurations.RunProfile; import com.intellij.execution.configurations.RunProfileState; @@ -26,6 +27,7 @@ import com.intellij.execution.impl.RunManagerImpl; import com.intellij.execution.impl.RunnerAndConfigurationSettingsImpl; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ExecutionEnvironmentBuilder; +import com.intellij.execution.runners.ProgramRunner; import com.intellij.execution.testframework.export.TestResultsXmlFormatter; import com.intellij.execution.testframework.sm.runner.history.ImportedTestRunnableState; import com.intellij.execution.testframework.sm.runner.SMRunnerConsolePropertiesProvider; @@ -115,7 +117,14 @@ public abstract class AbstractImportTestsAction extends AnAction { } final Executor executor = properties != null ? properties.getExecutor() : ExecutorRegistry.getInstance().getExecutorById(DefaultRunExecutor.EXECUTOR_ID); - ExecutionEnvironmentBuilder.create(project, executor, profile).buildAndExecute(); + ExecutionEnvironmentBuilder builder = ExecutionEnvironmentBuilder.create(project, executor, profile); + final RunConfiguration initialConfiguration = profile.getInitialConfiguration(); + final ProgramRunner runner = + initialConfiguration != null ? RunnerRegistry.getInstance().getRunner(executor.getId(), initialConfiguration) : null; + if (runner != null) { + builder = builder.runner(runner); + } + builder.buildAndExecute(); } catch (ExecutionException e1) { Messages.showErrorDialog(project, e1.getMessage(), "Import Failed"); @@ -214,6 +223,10 @@ public abstract class AbstractImportTestsAction extends AnAction { return myProperties; } + public RunConfiguration getInitialConfiguration() { + return mySettings != null ? mySettings.getConfiguration() : null; + } + public Project getProject() { return myProject; } diff --git a/platform/testRunner/src/com/intellij/execution/testframework/TestConsoleProperties.java b/platform/testRunner/src/com/intellij/execution/testframework/TestConsoleProperties.java index 7a94049bd415..5c8ab91ca7ca 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/TestConsoleProperties.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/TestConsoleProperties.java @@ -195,7 +195,7 @@ public abstract class TestConsoleProperties extends StoringPropertyContainer imp myUsePredefinedMessageFilter = usePredefinedMessageFilter; } - protected void appendAdditionalActions(DefaultActionGroup actionGroup, JComponent parent) { } + public void appendAdditionalActions(DefaultActionGroup actionGroup, JComponent parent) { } @Nullable protected AnAction createImportAction() { diff --git a/plugins/junit/src/com/intellij/execution/junit2/ui/properties/JUnitConsoleProperties.java b/plugins/junit/src/com/intellij/execution/junit2/ui/properties/JUnitConsoleProperties.java index 24fcb2a87aba..b6ee77a9639d 100644 --- a/plugins/junit/src/com/intellij/execution/junit2/ui/properties/JUnitConsoleProperties.java +++ b/plugins/junit/src/com/intellij/execution/junit2/ui/properties/JUnitConsoleProperties.java @@ -58,8 +58,8 @@ public class JUnitConsoleProperties extends JavaAwareTestConsoleProperties Date: Wed, 1 Jul 2015 20:08:36 +0300 Subject: [PATCH 036/137] import/export tests: include successful configs according to setting; allow to change view after import accordingly --- .../ImportedTestConsoleProperties.java | 5 ++-- .../history/ImportedTestContentHandler.java | 7 +++++- .../sm/runner/ui/SMTestRunnerResultsForm.java | 4 +++- .../testframework/TestConsoleProperties.java | 10 ++++---- .../execution/testframework/ToolbarPanel.java | 3 +-- .../export/ExportTestResultsAction.java | 2 +- .../export/TestResultsXmlFormatter.java | 23 ++++++++++++------- .../ui/properties/JUnitConsoleProperties.java | 7 +++--- .../testng/model/TestNGConsoleProperties.java | 9 ++++---- 9 files changed, 42 insertions(+), 28 deletions(-) diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/history/ImportedTestConsoleProperties.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/history/ImportedTestConsoleProperties.java index 4241ba73fa57..faba4f0d2e5a 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/history/ImportedTestConsoleProperties.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/history/ImportedTestConsoleProperties.java @@ -31,7 +31,6 @@ import com.intellij.execution.ui.ConsoleView; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.project.Project; import com.intellij.pom.Navigatable; -import com.intellij.util.config.ToggleBooleanProperty; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -116,9 +115,9 @@ public class ImportedTestConsoleProperties extends SMTRunnerConsoleProperties im } @Override - public void appendAdditionalActions(DefaultActionGroup actionGroup, JComponent parent) { + public void appendAdditionalActions(DefaultActionGroup actionGroup, JComponent parent, TestConsoleProperties target) { if (myProperties != null) { - myProperties.appendAdditionalActions(actionGroup, parent); + myProperties.appendAdditionalActions(actionGroup, parent, this); } } } diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/history/ImportedTestContentHandler.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/history/ImportedTestContentHandler.java index f00a994ba7cd..b949df18d78c 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/history/ImportedTestContentHandler.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/history/ImportedTestContentHandler.java @@ -52,7 +52,12 @@ public class ImportedTestContentHandler extends DefaultHandler { myCurrentTest = name; myDuration = attributes.getValue(TestResultsXmlFormatter.ATTR_DURATION); myStatus = attributes.getValue(TestResultsXmlFormatter.ATTR_STATUS); - myProcessor.onTestStarted(new TestStartedEvent(name, attributes.getValue(TestResultsXmlFormatter.ATTR_LOCATION))); + final String isConfig = attributes.getValue(TestResultsXmlFormatter.ATTR_CONFIG); + final TestStartedEvent startedEvent = new TestStartedEvent(name, attributes.getValue(TestResultsXmlFormatter.ATTR_LOCATION)); + if (isConfig != null && Boolean.valueOf(isConfig)) { + startedEvent.setConfig(true); + } + myProcessor.onTestStarted(startedEvent); currentValue.setLength(0); } else if (TestResultsXmlFormatter.ELEM_OUTPUT.equals(qName)) { diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/SMTestRunnerResultsForm.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/SMTestRunnerResultsForm.java index 99f70a67f5c6..e2ebf8500fd6 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/SMTestRunnerResultsForm.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/SMTestRunnerResultsForm.java @@ -777,12 +777,14 @@ public class SMTestRunnerResultsForm extends TestResultsPanel private static class MySaveHistoryTask extends Task.Backgroundable { + private final TestConsoleProperties myConsoleProperties; private SMTestProxy.SMRootTestProxy myRoot; private RunConfiguration myConfiguration; private String myOutput; public MySaveHistoryTask(TestConsoleProperties consoleProperties, SMTestProxy.SMRootTestProxy root, RunConfiguration configuration) { super(consoleProperties.getProject(), "Save Test Results", true); + myConsoleProperties = consoleProperties; myRoot = root; myConfiguration = configuration; } @@ -800,7 +802,7 @@ public class SMTestRunnerResultsForm extends TestResultsPanel final SMTestProxy.SMRootTestProxy root = myRoot; final RunConfiguration configuration = myConfiguration; if (root != null && configuration != null) { - TestResultsXmlFormatter.execute(root, configuration, handler); + TestResultsXmlFormatter.execute(root, configuration, myConsoleProperties, handler); } myOutput = w.toString(); } diff --git a/platform/testRunner/src/com/intellij/execution/testframework/TestConsoleProperties.java b/platform/testRunner/src/com/intellij/execution/testframework/TestConsoleProperties.java index 5c8ab91ca7ca..b756ba624505 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/TestConsoleProperties.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/TestConsoleProperties.java @@ -195,7 +195,7 @@ public abstract class TestConsoleProperties extends StoringPropertyContainer imp myUsePredefinedMessageFilter = usePredefinedMessageFilter; } - public void appendAdditionalActions(DefaultActionGroup actionGroup, JComponent parent) { } + public void appendAdditionalActions(DefaultActionGroup actionGroup, JComponent parent, TestConsoleProperties target) { } @Nullable protected AnAction createImportAction() { @@ -203,16 +203,16 @@ public abstract class TestConsoleProperties extends StoringPropertyContainer imp } @NotNull - protected ToggleBooleanProperty createIncludeNonStartedInRerun() { + protected ToggleBooleanProperty createIncludeNonStartedInRerun(TestConsoleProperties target) { String text = ExecutionBundle.message("junit.runing.info.include.non.started.in.rerun.failed.action.name"); - return new ToggleBooleanProperty(text, null, null, this, INCLUDE_NON_STARTED_IN_RERUN_FAILED); + return new ToggleBooleanProperty(text, null, null, target, INCLUDE_NON_STARTED_IN_RERUN_FAILED); } @NotNull - protected ToggleBooleanProperty createHideSuccessfulConfig() { + protected ToggleBooleanProperty createHideSuccessfulConfig(TestConsoleProperties target) { String text = ExecutionBundle.message("junit.runing.info.hide.successful.config.action.name"); setIfUndefined(HIDE_SUCCESSFUL_CONFIG, true); - return new ToggleBooleanProperty(text, null, null, this, HIDE_SUCCESSFUL_CONFIG); + return new ToggleBooleanProperty(text, null, null, target, HIDE_SUCCESSFUL_CONFIG); } @JdkConstants.TreeSelectionMode diff --git a/platform/testRunner/src/com/intellij/execution/testframework/ToolbarPanel.java b/platform/testRunner/src/com/intellij/execution/testframework/ToolbarPanel.java index d0f1dc44d6b4..6955be5cd2ba 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/ToolbarPanel.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/ToolbarPanel.java @@ -23,7 +23,6 @@ package com.intellij.execution.testframework; import com.intellij.execution.ExecutionBundle; import com.intellij.execution.configurations.RunConfiguration; import com.intellij.execution.configurations.RunProfile; -import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.testframework.actions.ScrollToTestSourceAction; import com.intellij.execution.testframework.actions.ShowStatisticsAction; import com.intellij.execution.testframework.actions.TestFrameworkActions; @@ -155,7 +154,7 @@ public class ToolbarPanel extends JPanel implements OccurenceNavigator, Disposab secondaryGroup.addSeparator(); secondaryGroup.add(new ToggleBooleanProperty(ExecutionBundle.message("junit.runing.info.select.first.failed.action.name"), null, null, properties, TestConsoleProperties.SELECT_FIRST_DEFECT)); - properties.appendAdditionalActions(secondaryGroup, parent); + properties.appendAdditionalActions(secondaryGroup, parent, properties); actionGroup.add(secondaryGroup); add(ActionManager.getInstance(). diff --git a/platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsAction.java b/platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsAction.java index 644ddfa40a67..331ee9e8791b 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsAction.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsAction.java @@ -307,7 +307,7 @@ public class ExportTestResultsAction extends DumbAwareAction { StringWriter w = new StringWriter(); handler.setResult(new StreamResult(w)); try { - TestResultsXmlFormatter.execute(myModel.getRoot(), myRunConfiguration, handler); + TestResultsXmlFormatter.execute(myModel.getRoot(), myRunConfiguration, myModel.getProperties(), handler); } catch (ProcessCanceledException e) { return null; diff --git a/platform/testRunner/src/com/intellij/execution/testframework/export/TestResultsXmlFormatter.java b/platform/testRunner/src/com/intellij/execution/testframework/export/TestResultsXmlFormatter.java index c12d8fc15dfb..ed93d178dc2a 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/export/TestResultsXmlFormatter.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/export/TestResultsXmlFormatter.java @@ -18,12 +18,10 @@ package com.intellij.execution.testframework.export; import com.intellij.execution.ExecutionBundle; import com.intellij.execution.configurations.RunConfiguration; import com.intellij.execution.filters.*; +import com.intellij.execution.filters.Filter; import com.intellij.execution.impl.RunManagerImpl; import com.intellij.execution.impl.RunnerAndConfigurationSettingsImpl; -import com.intellij.execution.testframework.AbstractTestProxy; -import com.intellij.execution.testframework.Printable; -import com.intellij.execution.testframework.Printer; -import com.intellij.execution.testframework.TestProxyRoot; +import com.intellij.execution.testframework.*; import com.intellij.execution.ui.ConsoleViewContentType; import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.extensions.Extensions; @@ -59,6 +57,7 @@ public class TestResultsXmlFormatter { public static final String ATTR_STATUS = "status"; public static final String TOTAL_STATUS = "total"; private static final String ATTR_FOORTER_TEXT = "footerText"; + public static final String ATTR_CONFIG = "isConfig"; public static final String STATUS_PASSED = "passed"; public static final String STATUS_FAILED = "failed"; public static final String STATUS_ERROR = "error"; @@ -72,16 +71,21 @@ public class TestResultsXmlFormatter { private final RunConfiguration myRuntimeConfiguration; private final ContentHandler myResultHandler; private final AbstractTestProxy myTestRoot; + private final boolean myHidePassedConfig; - public static void execute(AbstractTestProxy root, RunConfiguration runtimeConfiguration, ContentHandler resultHandler) + public static void execute(AbstractTestProxy root, RunConfiguration runtimeConfiguration, TestConsoleProperties properties, ContentHandler resultHandler) throws SAXException { - new TestResultsXmlFormatter(root, runtimeConfiguration, resultHandler).execute(); + new TestResultsXmlFormatter(root, runtimeConfiguration, properties, resultHandler).execute(); } - private TestResultsXmlFormatter(AbstractTestProxy root, RunConfiguration runtimeConfiguration, ContentHandler resultHandler) { + private TestResultsXmlFormatter(AbstractTestProxy root, + RunConfiguration runtimeConfiguration, + TestConsoleProperties properties, + ContentHandler resultHandler) { myRuntimeConfiguration = runtimeConfiguration; myTestRoot = root; myResultHandler = resultHandler; + myHidePassedConfig = TestConsoleProperties.HIDE_SUCCESSFUL_CONFIG.value(properties); } private void execute() throws SAXException { @@ -205,6 +209,9 @@ public class TestResultsXmlFormatter { if (locationUrl != null) { attrs.put(ATTR_LOCATION, locationUrl); } + if (node.isConfig()) { + attrs.put(ATTR_CONFIG, "true"); + } String elemName = node.isLeaf() ? ELEM_TEST : ELEM_SUITE; startElement(elemName, attrs); if (node.isLeaf()) { @@ -250,7 +257,7 @@ public class TestResultsXmlFormatter { } else { for (AbstractTestProxy child : node.getChildren()) { - if (child.isConfig() && child.isPassed()) { + if (myHidePassedConfig && child.isConfig() && child.isPassed()) { //ignore configurations during export continue; } diff --git a/plugins/junit/src/com/intellij/execution/junit2/ui/properties/JUnitConsoleProperties.java b/plugins/junit/src/com/intellij/execution/junit2/ui/properties/JUnitConsoleProperties.java index b6ee77a9639d..0bbde43947cb 100644 --- a/plugins/junit/src/com/intellij/execution/junit2/ui/properties/JUnitConsoleProperties.java +++ b/plugins/junit/src/com/intellij/execution/junit2/ui/properties/JUnitConsoleProperties.java @@ -21,6 +21,7 @@ import com.intellij.execution.junit2.ui.actions.RerunFailedTestsAction; import com.intellij.execution.testframework.JavaAwareTestConsoleProperties; import com.intellij.execution.testframework.JavaTestLocator; import com.intellij.execution.testframework.SourceScope; +import com.intellij.execution.testframework.TestConsoleProperties; import com.intellij.execution.testframework.actions.AbstractRerunFailedTestsAction; import com.intellij.execution.testframework.sm.runner.SMTestLocator; import com.intellij.execution.ui.ConsoleView; @@ -59,9 +60,9 @@ public class JUnitConsoleProperties extends JavaAwareTestConsoleProperties Date: Wed, 1 Jul 2015 20:36:26 +0300 Subject: [PATCH 037/137] testng: hide suite with only config methods if hide successful configs is on --- .../com/intellij/execution/testframework/Filter.java | 12 ++++++++++-- .../testframework/actions/TestFrameworkActions.java | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/platform/testRunner/src/com/intellij/execution/testframework/Filter.java b/platform/testRunner/src/com/intellij/execution/testframework/Filter.java index d4c6b1f9105a..6c35b7891da5 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/Filter.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/Filter.java @@ -127,10 +127,18 @@ public abstract class Filter { } }); - public static final Filter SUCCESSFUL_CONFIGS = new Filter() { + public static final Filter HIDE_SUCCESSFUL_CONFIGS = new Filter() { @Override public boolean shouldAccept(AbstractTestProxy test) { - return !test.isConfig() || !test.isPassed(); + final List children = test.getChildren(); + if (!children.isEmpty()) { + for (AbstractTestProxy proxy : children) { + if (!proxy.isConfig() || !proxy.isPassed()) return true; + } + return false; + } + + return !(test.isConfig() && test.isPassed()); } }; diff --git a/platform/testRunner/src/com/intellij/execution/testframework/actions/TestFrameworkActions.java b/platform/testRunner/src/com/intellij/execution/testframework/actions/TestFrameworkActions.java index 1ee357d4d0d0..712b1bdb483a 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/actions/TestFrameworkActions.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/actions/TestFrameworkActions.java @@ -56,7 +56,7 @@ public class TestFrameworkActions { } final boolean hideSuccessfulConfigs = TestConsoleProperties.HIDE_SUCCESSFUL_CONFIG.value(properties); - final Filter hideConfigsFilter = hideSuccessfulConfigs ? Filter.SUCCESSFUL_CONFIGS : Filter.NO_FILTER; + final Filter hideConfigsFilter = hideSuccessfulConfigs ? Filter.HIDE_SUCCESSFUL_CONFIGS : Filter.NO_FILTER; return hidePassedFilter.and(hideIgnoredFilter).and(hideConfigsFilter); } From fd33468767ba5c02e58971f0429054b337b74eb1 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Wed, 1 Jul 2015 21:29:22 +0300 Subject: [PATCH 038/137] junit: fully ignore test class with @Ignore annotation --- .../com/intellij/junit4/JUnit4TestListener.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java b/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java index 6bbe7bd28b9f..6d4e5e33fb7e 100644 --- a/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java +++ b/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java @@ -211,6 +211,19 @@ public class JUnit4TestListener extends RunListener { } public synchronized void testIgnored(Description description) throws Exception { + final String methodName = getFullMethodName(description); + if (methodName == null) { + for (Iterator iterator = description.getChildren().iterator(); iterator.hasNext(); ) { + final Description testDescription = (Description)iterator.next(); + testIgnored(testDescription, getFullMethodName(testDescription)); + } + } + else { + testIgnored(description, methodName); + } + } + + private void testIgnored(Description description, String methodName) throws Exception { testStarted(description); Map attrs = new HashMap(); try { @@ -225,7 +238,7 @@ public class JUnit4TestListener extends RunListener { catch (NoSuchMethodError ignored) { //junit < 4.4 } - attrs.put("name", getFullMethodName(description)); + attrs.put("name", methodName); myPrintStream.println(MapSerializerUtil.asString(MapSerializerUtil.TEST_IGNORED, attrs)); testFinished(description); } From c0f23414de09d99477544081b0856ba3da4ffc5c Mon Sep 17 00:00:00 2001 From: "Gregory.Shrago" Date: Wed, 1 Jul 2015 22:01:01 +0300 Subject: [PATCH 039/137] close-then-save to avoid confusion as save may take some time --- .../options/ex/SingleConfigurableEditor.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/SingleConfigurableEditor.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/SingleConfigurableEditor.java index f114fc2becad..612715b43bfa 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/ex/SingleConfigurableEditor.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/ex/SingleConfigurableEditor.java @@ -47,7 +47,7 @@ public class SingleConfigurableEditor extends DialogWrapper { private JComponent myCenterPanel; private final String myDimensionKey; private final boolean myShowApplyButton; - private boolean myChangesWereApplied; + private boolean mySaveAllOnClose; public SingleConfigurableEditor(@Nullable Project project, Configurable configurable, @@ -166,18 +166,16 @@ public class SingleConfigurableEditor extends DialogWrapper { @Override public void doCancelAction() { - if (myChangesWereApplied) { - ApplicationManager.getApplication().saveAll(); - } super.doCancelAction(); } @Override protected void doOKAction() { try { - if (myConfigurable.isModified()) myConfigurable.apply(); - - ApplicationManager.getApplication().saveAll(); + if (myConfigurable.isModified()) { + myConfigurable.apply(); + mySaveAllOnClose = true; + } } catch (ConfigurationException e) { if (e.getMessage() != null) { @@ -237,7 +235,7 @@ public class SingleConfigurableEditor extends DialogWrapper { myPerformAction = true; if (myConfigurable.isModified()) { myConfigurable.apply(); - myChangesWereApplied = true; + mySaveAllOnClose = true; setCancelButtonText(CommonBundle.getCloseButtonText()); } } @@ -275,5 +273,9 @@ public class SingleConfigurableEditor extends DialogWrapper { super.dispose(); myConfigurable.disposeUIResources(); myConfigurable = null; + + if (mySaveAllOnClose) { + ApplicationManager.getApplication().saveAll(); + } } } From a6e21812c010d9829498b42ece0f3322ec6403c4 Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Wed, 1 Jul 2015 21:28:19 +0200 Subject: [PATCH 040/137] IDEA-142182 File templates variables table size is inconsistent --- .../Groovy JUnit SetUp Method.groovy.html | 4 +-- .../Groovy JUnit TearDown Method.groovy.html | 4 +-- .../code/Groovy JUnit Test Case.groovy.html | 4 +-- .../code/Groovy JUnit Test Method.groovy.html | 4 +-- .../code/Groovy New Method Body.groovy.html | 10 +++---- .../code/Spock Test Method.groovy.html | 4 +-- .../code/Spock cleanup Method.groovy.html | 4 +-- .../code/Spock_SetUp_Method.groovy.html | 4 +-- .../code/JUnit3 SetUp Method.java.html | 4 +-- .../code/JUnit3 TearDown Method.java.html | 4 +-- .../code/JUnit3 Test Class.java.html | 6 ++--- .../code/JUnit3 Test Method.java.html | 4 +-- .../code/JUnit4 Parameters Method.java.html | 4 +-- .../code/JUnit4 SetUp Method.java.html | 4 +-- .../code/JUnit4 TearDown Method.java.html | 4 +-- .../code/JUnit4 Test Class.java.html | 6 ++--- .../code/JUnit4 Test Method.java.html | 4 +-- .../code/TestNG Parameters Method.java.html | 4 +-- .../code/TestNG SetUp Method.java.html | 4 +-- .../code/TestNG TearDown Method.java.html | 4 +-- .../code/TestNG Test Class.java.html | 6 ++--- .../code/TestNG Test Method.java.html | 4 +-- .../code/Catch Statement Body.java.html | 4 +-- .../code/I18nized Concatenation.java.html | 8 +++--- .../code/I18nized Expression.java.html | 6 ++--- .../code/I18nized JSP Expression.jsp.html | 4 +-- .../code/Implemented Method Body.java.html | 10 +++---- .../code/New Method Body.java.html | 10 +++---- .../code/Overridden Method Body.java.html | 12 ++++----- resources-en/src/fileTemplates/default.html | 26 +++++++++---------- .../includes/File Header.java.html | 24 ++++++++--------- .../src/fileTemplates/includes/default.html | 24 ++++++++--------- .../internal/AnnotationType.java.html | 26 +++++++++---------- .../fileTemplates/internal/Class.java.html | 26 +++++++++---------- .../src/fileTemplates/internal/Enum.java.html | 26 +++++++++---------- .../internal/Interface.java.html | 26 +++++++++---------- .../internal/package-info.java.html | 24 ++++++++--------- 37 files changed, 178 insertions(+), 178 deletions(-) diff --git a/plugins/groovy/groovy-psi/resources/fileTemplates/code/Groovy JUnit SetUp Method.groovy.html b/plugins/groovy/groovy-psi/resources/fileTemplates/code/Groovy JUnit SetUp Method.groovy.html index 59bf56df03e5..3b29eed03d8d 100644 --- a/plugins/groovy/groovy-psi/resources/fileTemplates/code/Groovy JUnit SetUp Method.groovy.html +++ b/plugins/groovy/groovy-psi/resources/fileTemplates/code/Groovy JUnit SetUp Method.groovy.html @@ -15,12 +15,12 @@ ${NAME}   - name of the created method. + name of the created method. ${BODY}   - generated method body. + generated method body. diff --git a/plugins/groovy/groovy-psi/resources/fileTemplates/code/Groovy JUnit TearDown Method.groovy.html b/plugins/groovy/groovy-psi/resources/fileTemplates/code/Groovy JUnit TearDown Method.groovy.html index b31948642b23..08b75d0b30b4 100644 --- a/plugins/groovy/groovy-psi/resources/fileTemplates/code/Groovy JUnit TearDown Method.groovy.html +++ b/plugins/groovy/groovy-psi/resources/fileTemplates/code/Groovy JUnit TearDown Method.groovy.html @@ -15,12 +15,12 @@ ${NAME}   - name of the created method. + name of the created method. ${BODY}   - generated method body. + generated method body. diff --git a/plugins/groovy/groovy-psi/resources/fileTemplates/code/Groovy JUnit Test Case.groovy.html b/plugins/groovy/groovy-psi/resources/fileTemplates/code/Groovy JUnit Test Case.groovy.html index d5fe228ec24e..de03ccf75d81 100644 --- a/plugins/groovy/groovy-psi/resources/fileTemplates/code/Groovy JUnit Test Case.groovy.html +++ b/plugins/groovy/groovy-psi/resources/fileTemplates/code/Groovy JUnit Test Case.groovy.html @@ -15,12 +15,12 @@ ${NAME}   - name of the created class. + name of the created class. ${BODY}   - generated class body. + generated class body. diff --git a/plugins/groovy/groovy-psi/resources/fileTemplates/code/Groovy JUnit Test Method.groovy.html b/plugins/groovy/groovy-psi/resources/fileTemplates/code/Groovy JUnit Test Method.groovy.html index 2f27795254f2..adb72f10c452 100644 --- a/plugins/groovy/groovy-psi/resources/fileTemplates/code/Groovy JUnit Test Method.groovy.html +++ b/plugins/groovy/groovy-psi/resources/fileTemplates/code/Groovy JUnit Test Method.groovy.html @@ -15,12 +15,12 @@ ${NAME}   - name of the created method. + name of the created method. ${BODY}   - generated method body. + generated method body. diff --git a/plugins/groovy/groovy-psi/resources/fileTemplates/code/Groovy New Method Body.groovy.html b/plugins/groovy/groovy-psi/resources/fileTemplates/code/Groovy New Method Body.groovy.html index a98243ef53ce..411a720471ae 100644 --- a/plugins/groovy/groovy-psi/resources/fileTemplates/code/Groovy New Method Body.groovy.html +++ b/plugins/groovy/groovy-psi/resources/fileTemplates/code/Groovy New Method Body.groovy.html @@ -16,27 +16,27 @@ ${RETURN_TYPE}   - a return type of a created method + a return type of a created method ${DEFAULT_RETURN_VALUE}   - a value returned by the method by default + a value returned by the method by default ${METHOD_NAME}   - name of the created method + name of the created method ${CLASS_NAME}   - qualified name of the class where method is created + qualified name of the class where method is created ${SIMPLE_CLASS_NAME}   - non-qualified name of the class where method is implemented + non-qualified name of the class where method is implemented diff --git a/plugins/groovy/groovy-psi/resources/fileTemplates/code/Spock Test Method.groovy.html b/plugins/groovy/groovy-psi/resources/fileTemplates/code/Spock Test Method.groovy.html index 8a162f4ad3a8..19225e3347eb 100644 --- a/plugins/groovy/groovy-psi/resources/fileTemplates/code/Spock Test Method.groovy.html +++ b/plugins/groovy/groovy-psi/resources/fileTemplates/code/Spock Test Method.groovy.html @@ -15,12 +15,12 @@ ${NAME}   - name of the created method. + name of the created method. ${BODY}   - generated method body. + generated method body. diff --git a/plugins/groovy/groovy-psi/resources/fileTemplates/code/Spock cleanup Method.groovy.html b/plugins/groovy/groovy-psi/resources/fileTemplates/code/Spock cleanup Method.groovy.html index 538317fa6376..aa8a3b993a57 100644 --- a/plugins/groovy/groovy-psi/resources/fileTemplates/code/Spock cleanup Method.groovy.html +++ b/plugins/groovy/groovy-psi/resources/fileTemplates/code/Spock cleanup Method.groovy.html @@ -15,12 +15,12 @@ ${NAME}   - name of the created method. + name of the created method. ${BODY}   - generated method body. + generated method body. diff --git a/plugins/groovy/groovy-psi/resources/fileTemplates/code/Spock_SetUp_Method.groovy.html b/plugins/groovy/groovy-psi/resources/fileTemplates/code/Spock_SetUp_Method.groovy.html index 0ae62ac4e8b8..af1715cabe1e 100644 --- a/plugins/groovy/groovy-psi/resources/fileTemplates/code/Spock_SetUp_Method.groovy.html +++ b/plugins/groovy/groovy-psi/resources/fileTemplates/code/Spock_SetUp_Method.groovy.html @@ -15,12 +15,12 @@ ${NAME}   - name of the created method. + name of the created method. ${BODY}   - generated method body. + generated method body. diff --git a/plugins/junit/src/fileTemplates/code/JUnit3 SetUp Method.java.html b/plugins/junit/src/fileTemplates/code/JUnit3 SetUp Method.java.html index d41bedc88e66..da9481ce7e71 100644 --- a/plugins/junit/src/fileTemplates/code/JUnit3 SetUp Method.java.html +++ b/plugins/junit/src/fileTemplates/code/JUnit3 SetUp Method.java.html @@ -15,12 +15,12 @@ ${NAME}   - name of the created method. + name of the created method. ${BODY}   - generated method body. + generated method body. diff --git a/plugins/junit/src/fileTemplates/code/JUnit3 TearDown Method.java.html b/plugins/junit/src/fileTemplates/code/JUnit3 TearDown Method.java.html index a20a8a351f81..af95f9eb9f0d 100644 --- a/plugins/junit/src/fileTemplates/code/JUnit3 TearDown Method.java.html +++ b/plugins/junit/src/fileTemplates/code/JUnit3 TearDown Method.java.html @@ -15,12 +15,12 @@ ${NAME}   - name of the created method. + name of the created method. ${BODY}   - generated method body. + generated method body. diff --git a/plugins/junit/src/fileTemplates/code/JUnit3 Test Class.java.html b/plugins/junit/src/fileTemplates/code/JUnit3 Test Class.java.html index fa8f461d46c9..55a3babbf7cc 100644 --- a/plugins/junit/src/fileTemplates/code/JUnit3 Test Class.java.html +++ b/plugins/junit/src/fileTemplates/code/JUnit3 Test Class.java.html @@ -14,17 +14,17 @@ ${NAME}   - name of the created class. + name of the created class. ${CLASS_NAME}   - name of the tested class. + name of the tested class. ${BODY}   - generated class body. + generated class body. diff --git a/plugins/junit/src/fileTemplates/code/JUnit3 Test Method.java.html b/plugins/junit/src/fileTemplates/code/JUnit3 Test Method.java.html index 738f32f0401f..2c5b0884c599 100644 --- a/plugins/junit/src/fileTemplates/code/JUnit3 Test Method.java.html +++ b/plugins/junit/src/fileTemplates/code/JUnit3 Test Method.java.html @@ -15,12 +15,12 @@ ${NAME}   - name of the created method. + name of the created method. ${BODY}   - generated method body. + generated method body. diff --git a/plugins/junit/src/fileTemplates/code/JUnit4 Parameters Method.java.html b/plugins/junit/src/fileTemplates/code/JUnit4 Parameters Method.java.html index 7844aa2260b0..84955b94bb57 100644 --- a/plugins/junit/src/fileTemplates/code/JUnit4 Parameters Method.java.html +++ b/plugins/junit/src/fileTemplates/code/JUnit4 Parameters Method.java.html @@ -14,12 +14,12 @@ ${NAME}   - name of the created method. + name of the created method. ${BODY}   - generated method body. + generated method body. diff --git a/plugins/junit/src/fileTemplates/code/JUnit4 SetUp Method.java.html b/plugins/junit/src/fileTemplates/code/JUnit4 SetUp Method.java.html index b48a5a892f58..3108d0d2d617 100644 --- a/plugins/junit/src/fileTemplates/code/JUnit4 SetUp Method.java.html +++ b/plugins/junit/src/fileTemplates/code/JUnit4 SetUp Method.java.html @@ -14,12 +14,12 @@ ${NAME}   - name of the created method. + name of the created method. ${BODY}   - generated method body. + generated method body. diff --git a/plugins/junit/src/fileTemplates/code/JUnit4 TearDown Method.java.html b/plugins/junit/src/fileTemplates/code/JUnit4 TearDown Method.java.html index 199a4ec1e722..9f1134fc5285 100644 --- a/plugins/junit/src/fileTemplates/code/JUnit4 TearDown Method.java.html +++ b/plugins/junit/src/fileTemplates/code/JUnit4 TearDown Method.java.html @@ -14,12 +14,12 @@ ${NAME}   - name of the created method. + name of the created method. ${BODY}   - generated method body. + generated method body. diff --git a/plugins/junit/src/fileTemplates/code/JUnit4 Test Class.java.html b/plugins/junit/src/fileTemplates/code/JUnit4 Test Class.java.html index 17c69ded9d16..d69748372fd8 100644 --- a/plugins/junit/src/fileTemplates/code/JUnit4 Test Class.java.html +++ b/plugins/junit/src/fileTemplates/code/JUnit4 Test Class.java.html @@ -14,17 +14,17 @@ ${NAME}   - name of the created class. + name of the created class. ${CLASS_NAME}   - name of the tested class. + name of the tested class. ${BODY}   - generated class body. + generated class body. diff --git a/plugins/junit/src/fileTemplates/code/JUnit4 Test Method.java.html b/plugins/junit/src/fileTemplates/code/JUnit4 Test Method.java.html index 1b7bde93b677..2bc910979a2d 100644 --- a/plugins/junit/src/fileTemplates/code/JUnit4 Test Method.java.html +++ b/plugins/junit/src/fileTemplates/code/JUnit4 Test Method.java.html @@ -14,12 +14,12 @@ ${NAME}   - name of the created method. + name of the created method. ${BODY}   - generated method body. + generated method body. diff --git a/plugins/testng/src/fileTemplates/code/TestNG Parameters Method.java.html b/plugins/testng/src/fileTemplates/code/TestNG Parameters Method.java.html index da1766197222..4096b1bbec5a 100644 --- a/plugins/testng/src/fileTemplates/code/TestNG Parameters Method.java.html +++ b/plugins/testng/src/fileTemplates/code/TestNG Parameters Method.java.html @@ -14,12 +14,12 @@ ${NAME}   - name of the created method. + name of the created method. ${BODY}   - generated method body. + generated method body. diff --git a/plugins/testng/src/fileTemplates/code/TestNG SetUp Method.java.html b/plugins/testng/src/fileTemplates/code/TestNG SetUp Method.java.html index 9736ca8b96e1..83e384012d4c 100644 --- a/plugins/testng/src/fileTemplates/code/TestNG SetUp Method.java.html +++ b/plugins/testng/src/fileTemplates/code/TestNG SetUp Method.java.html @@ -14,12 +14,12 @@ ${NAME}   - name of the created method. + name of the created method. ${BODY}   - generated method body. + generated method body. diff --git a/plugins/testng/src/fileTemplates/code/TestNG TearDown Method.java.html b/plugins/testng/src/fileTemplates/code/TestNG TearDown Method.java.html index f7f3b58393d1..db29f0bf12b9 100644 --- a/plugins/testng/src/fileTemplates/code/TestNG TearDown Method.java.html +++ b/plugins/testng/src/fileTemplates/code/TestNG TearDown Method.java.html @@ -14,12 +14,12 @@ ${NAME}   - name of the created method. + name of the created method. ${BODY}   - generated method body. + generated method body. diff --git a/plugins/testng/src/fileTemplates/code/TestNG Test Class.java.html b/plugins/testng/src/fileTemplates/code/TestNG Test Class.java.html index f068ea33a622..4fbd57c7ce0e 100644 --- a/plugins/testng/src/fileTemplates/code/TestNG Test Class.java.html +++ b/plugins/testng/src/fileTemplates/code/TestNG Test Class.java.html @@ -14,17 +14,17 @@ ${NAME}   - name of the created class. + name of the created class. ${CLASS_NAME}   - name of the tested class. + name of the tested class. ${BODY}   - generated class body. + generated class body. diff --git a/plugins/testng/src/fileTemplates/code/TestNG Test Method.java.html b/plugins/testng/src/fileTemplates/code/TestNG Test Method.java.html index 81b82b324de1..19e8b49beb94 100644 --- a/plugins/testng/src/fileTemplates/code/TestNG Test Method.java.html +++ b/plugins/testng/src/fileTemplates/code/TestNG Test Method.java.html @@ -14,12 +14,12 @@ ${NAME}   - name of the created method. + name of the created method. ${BODY}   - generated method body. + generated method body. diff --git a/resources-en/src/fileTemplates/code/Catch Statement Body.java.html b/resources-en/src/fileTemplates/code/Catch Statement Body.java.html index 06712437b6e9..4b521ae28a86 100644 --- a/resources-en/src/fileTemplates/code/Catch Statement Body.java.html +++ b/resources-en/src/fileTemplates/code/Catch Statement Body.java.html @@ -17,12 +17,12 @@ ${EXCEPTION}   - name of the Exception variable specified as a catch parameter + name of the Exception variable specified as a catch parameter ${EXCEPTION_TYPE}   - type of the catch parameter + type of the catch parameter diff --git a/resources-en/src/fileTemplates/code/I18nized Concatenation.java.html b/resources-en/src/fileTemplates/code/I18nized Concatenation.java.html index 5eac1093e7e0..882c67d95e9a 100644 --- a/resources-en/src/fileTemplates/code/I18nized Concatenation.java.html +++ b/resources-en/src/fileTemplates/code/I18nized Concatenation.java.html @@ -18,24 +18,24 @@ ${RESOURCE_BUNDLE}   - Expression of type java.util.ResourceBundle, instance of which is available in this context. + Expression of type java.util.ResourceBundle, instance of which is available in this context. ${PARAMETERS}   - Variables used in the string concatenation, usually passed as arguments to the internationalized expression. + Variables used in the string concatenation, usually passed as arguments to the internationalized expression. ${PROPERTY_KEY}   - Property key name which is defined in the corresponding properties file. + Property key name which is defined in the corresponding properties file. Typically it is the value that is used for ResourceBundle.getString() method parameter. ${PROPERTY_VALUE}   - Property value which is defined in the corresponding properties file. + Property value which is defined in the corresponding properties file. This is the original Java string literal value. diff --git a/resources-en/src/fileTemplates/code/I18nized Expression.java.html b/resources-en/src/fileTemplates/code/I18nized Expression.java.html index 9b6decffb14e..c2bdc0bb9666 100644 --- a/resources-en/src/fileTemplates/code/I18nized Expression.java.html +++ b/resources-en/src/fileTemplates/code/I18nized Expression.java.html @@ -18,19 +18,19 @@ ${RESOURCE_BUNDLE}   - Expression of type java.util.ResourceBundle, instance of which is available in this context. + Expression of type java.util.ResourceBundle, instance of which is available in this context. ${PROPERTY_KEY}   - Property key name which is specified in the corresponding properties file. + Property key name which is specified in the corresponding properties file. Typically it is the value that is used for ResourceBundle.getString() method parameter. ${PROPERTY_VALUE}   - Property value which is defined in the corresponding properties file. + Property value which is defined in the corresponding properties file. This is the original Java string literal value. diff --git a/resources-en/src/fileTemplates/code/I18nized JSP Expression.jsp.html b/resources-en/src/fileTemplates/code/I18nized JSP Expression.jsp.html index 7ee26d1ee527..ddbdf586ebb1 100644 --- a/resources-en/src/fileTemplates/code/I18nized JSP Expression.jsp.html +++ b/resources-en/src/fileTemplates/code/I18nized JSP Expression.jsp.html @@ -28,13 +28,13 @@ ${PROPERTY_KEY}   - Property key name which is specified in the corresponding properties file. + Property key name which is specified in the corresponding properties file. ${PROPERTY_VALUE}   - Property value which is specified in the corresponding properties file. + Property value which is specified in the corresponding properties file. This is an original JSP text selection value. diff --git a/resources-en/src/fileTemplates/code/Implemented Method Body.java.html b/resources-en/src/fileTemplates/code/Implemented Method Body.java.html index 3e9ab3db7b87..5ace03af656b 100644 --- a/resources-en/src/fileTemplates/code/Implemented Method Body.java.html +++ b/resources-en/src/fileTemplates/code/Implemented Method Body.java.html @@ -18,27 +18,27 @@ ${RETURN_TYPE}   - return type of a created method + return type of a created method ${DEFAULT_RETURN_VALUE}   - value returned by the method by default + value returned by the method by default ${METHOD_NAME}   - name of the method that is implemented + name of the method that is implemented ${CLASS_NAME}   - qualified name of the class where method is implemented + qualified name of the class where method is implemented ${SIMPLE_CLASS_NAME}   - non-qualified name of the class where method is implemented + non-qualified name of the class where method is implemented All the predefined variables from the File Header template (Includes tab) are also available diff --git a/resources-en/src/fileTemplates/code/New Method Body.java.html b/resources-en/src/fileTemplates/code/New Method Body.java.html index 73d7741a07ee..9510ae63d65c 100644 --- a/resources-en/src/fileTemplates/code/New Method Body.java.html +++ b/resources-en/src/fileTemplates/code/New Method Body.java.html @@ -16,27 +16,27 @@ ${RETURN_TYPE}   - return type of a created method + return type of a created method ${DEFAULT_RETURN_VALUE}   - value returned by the method by default + value returned by the method by default ${METHOD_NAME}   - name of the created method + name of the created method ${CLASS_NAME}   - qualified name of the class where method is created + qualified name of the class where method is created ${SIMPLE_CLASS_NAME}   - non-qualified name of the class where method is implemented + non-qualified name of the class where method is implemented All the predefined variables from the File Header template (Includes tab) are also available diff --git a/resources-en/src/fileTemplates/code/Overridden Method Body.java.html b/resources-en/src/fileTemplates/code/Overridden Method Body.java.html index fe9e5ef4d871..bf6621df421f 100644 --- a/resources-en/src/fileTemplates/code/Overridden Method Body.java.html +++ b/resources-en/src/fileTemplates/code/Overridden Method Body.java.html @@ -17,34 +17,34 @@ ${CALL_SUPER}   - a super method call, for void methods - super.method_name(); with or without parameters; + a super method call, for void methods - super.method_name(); with or without parameters; for other methods - return super.method_name(); with or without parameters ${RETURN_TYPE}   - return type of a created method + return type of a created method ${DEFAULT_RETURN_VALUE}   - value returned by the method by default + value returned by the method by default ${METHOD_NAME}   - name of the method that is overridden + name of the method that is overridden ${CLASS_NAME}   - qualified name of the class where method is overridden + qualified name of the class where method is overridden ${SIMPLE_CLASS_NAME}   - non-qualified name of the class where method is implemented + non-qualified name of the class where method is implemented All the predefined variables from the File Header template (Includes tab) are also available diff --git a/resources-en/src/fileTemplates/default.html b/resources-en/src/fileTemplates/default.html index 3f274894834c..a3edd5bf792a 100644 --- a/resources-en/src/fileTemplates/default.html +++ b/resources-en/src/fileTemplates/default.html @@ -22,67 +22,67 @@ ${PACKAGE_NAME}   - name of the package in which the new file is created + name of the package in which the new file is created ${NAME}   - name of the new file specified by you in the New <TEMPLATE_NAME> dialog + name of the new file specified by you in the New <TEMPLATE_NAME> dialog ${USER}   - current user system login name + current user system login name ${DATE}   - current system date + current system date ${TIME}   - current system time + current system time ${YEAR}   - current year + current year ${MONTH}   - current month + current month ${MONTH_NAME_SHORT}   - first 3 letters of the current month name. Example: Jan, Feb, etc. + first 3 letters of the current month name. Example: Jan, Feb, etc. ${MONTH_NAME_FULL}   - full name of the current month. Example: January, February, etc. + full name of the current month. Example: January, February, etc. ${DAY}   - current day of the month + current day of the month ${HOUR}   - current hour + current hour ${MINUTE}   - current minute + current minute ${PROJECT_NAME}   - the name of the current project + the name of the current project diff --git a/resources-en/src/fileTemplates/includes/File Header.java.html b/resources-en/src/fileTemplates/includes/File Header.java.html index 620246af9ae1..30b9700b873d 100644 --- a/resources-en/src/fileTemplates/includes/File Header.java.html +++ b/resources-en/src/fileTemplates/includes/File Header.java.html @@ -16,62 +16,62 @@ ${PACKAGE_NAME}   - name of the package in which the new file is created + name of the package in which the new file is created ${USER}   - current user system login name + current user system login name ${DATE}   - current system date + current system date ${TIME}   - current system time + current system time ${YEAR}   - current year + current year ${MONTH}   - current month + current month ${MONTH_NAME_SHORT}   - first 3 letters of the current month name. Example: Jan, Feb, etc. + first 3 letters of the current month name. Example: Jan, Feb, etc. ${MONTH_NAME_FULL}   - full name of the current month. Example: January, February, etc. + full name of the current month. Example: January, February, etc. ${DAY}   - current day of the month + current day of the month ${HOUR}   - current hour + current hour ${MINUTE}   - current minute + current minute ${PROJECT_NAME}   - the name of the current project + the name of the current project diff --git a/resources-en/src/fileTemplates/includes/default.html b/resources-en/src/fileTemplates/includes/default.html index 0a6692ce8c10..372c29c20d90 100644 --- a/resources-en/src/fileTemplates/includes/default.html +++ b/resources-en/src/fileTemplates/includes/default.html @@ -17,62 +17,62 @@ ${PACKAGE_NAME}   - name of the package in which the new file is created + name of the package in which the new file is created ${USER}   - current user system login name + current user system login name ${DATE}   - current system date + current system date ${TIME}   - current system time + current system time ${YEAR}   - current year + current year ${MONTH}   - current month + current month ${MONTH_NAME_SHORT}   - first 3 letters of the current month name. Example: Jan, Feb, etc. + first 3 letters of the current month name. Example: Jan, Feb, etc. ${MONTH_NAME_FULL}   - full name of the current month. Example: January, February, etc. + full name of the current month. Example: January, February, etc. ${DAY}   - current day of the month + current day of the month ${HOUR}   - current hour + current hour ${MINUTE}   - current minute + current minute ${PROJECT_NAME}   - the name of the current project + the name of the current project diff --git a/resources-en/src/fileTemplates/internal/AnnotationType.java.html b/resources-en/src/fileTemplates/internal/AnnotationType.java.html index 9a9f1cda42df..9e5bd43ed021 100644 --- a/resources-en/src/fileTemplates/internal/AnnotationType.java.html +++ b/resources-en/src/fileTemplates/internal/AnnotationType.java.html @@ -24,67 +24,67 @@ ${PACKAGE_NAME}   - name of the package in which the new annotation is created + name of the package in which the new annotation is created ${NAME}   - name of the new annotation specified by you in the Create New Class dialog + name of the new annotation specified by you in the Create New Class dialog ${USER}   - current user system login name + current user system login name ${DATE}   - current system date + current system date ${TIME}   - current system time + current system time ${YEAR}   - current year + current year ${MONTH}   - current month + current month ${MONTH_NAME_SHORT}   - first 3 letters of the current month name. Example: Jan, Feb, etc. + first 3 letters of the current month name. Example: Jan, Feb, etc. ${MONTH_NAME_FULL}   - full name of the current month. Example: January, February, etc. + full name of the current month. Example: January, February, etc. ${DAY}   - current day of the month + current day of the month ${HOUR}   - current hour + current hour ${MINUTE}   - current minute + current minute ${PROJECT_NAME}   - the name of the current project + the name of the current project diff --git a/resources-en/src/fileTemplates/internal/Class.java.html b/resources-en/src/fileTemplates/internal/Class.java.html index a997ed46bf96..b9e263e38536 100644 --- a/resources-en/src/fileTemplates/internal/Class.java.html +++ b/resources-en/src/fileTemplates/internal/Class.java.html @@ -24,67 +24,67 @@ ${PACKAGE_NAME}   - name of the package in which the new class is created + name of the package in which the new class is created ${NAME}   - name of the new class specified by you in the Create New Class dialog + name of the new class specified by you in the Create New Class dialog ${USER}   - current user system login name + current user system login name ${DATE}   - current system date + current system date ${TIME}   - current system time + current system time ${YEAR}   - current year + current year ${MONTH}   - current month + current month ${MONTH_NAME_SHORT}   - first 3 letters of the current month name. Example: Jan, Feb, etc. + first 3 letters of the current month name. Example: Jan, Feb, etc. ${MONTH_NAME_FULL}   - full name of the current month. Example: January, February, etc. + full name of the current month. Example: January, February, etc. ${DAY}   - current day of the month + current day of the month ${HOUR}   - current hour + current hour ${MINUTE}   - current minute + current minute ${PROJECT_NAME}   - the name of the current project + the name of the current project diff --git a/resources-en/src/fileTemplates/internal/Enum.java.html b/resources-en/src/fileTemplates/internal/Enum.java.html index be18942b1cb6..2ef442b8fcc5 100644 --- a/resources-en/src/fileTemplates/internal/Enum.java.html +++ b/resources-en/src/fileTemplates/internal/Enum.java.html @@ -24,67 +24,67 @@ ${PACKAGE_NAME}   - name of the package in which the new enum is created + name of the package in which the new enum is created ${NAME}   - name of the new enum specified by you in the Create New Class dialog + name of the new enum specified by you in the Create New Class dialog ${USER}   - current user system login name + current user system login name ${DATE}   - current system date + current system date ${TIME}   - current system time + current system time ${YEAR}   - current year + current year ${MONTH}   - current month + current month ${MONTH_NAME_SHORT}   - first 3 letters of the current month name. Example: Jan, Feb, etc. + first 3 letters of the current month name. Example: Jan, Feb, etc. ${MONTH_NAME_FULL}   - full name of the current month. Example: January, February, etc. + full name of the current month. Example: January, February, etc. ${DAY}   - current day of the month + current day of the month ${HOUR}   - current hour + current hour ${MINUTE}   - current minute + current minute ${PROJECT_NAME}   - the name of the current project + the name of the current project diff --git a/resources-en/src/fileTemplates/internal/Interface.java.html b/resources-en/src/fileTemplates/internal/Interface.java.html index b172b381ff20..c1f0bbc67755 100644 --- a/resources-en/src/fileTemplates/internal/Interface.java.html +++ b/resources-en/src/fileTemplates/internal/Interface.java.html @@ -24,67 +24,67 @@ ${PACKAGE_NAME}   - name of the package in which the new interface is created + name of the package in which the new interface is created ${NAME}   - name of the new interface specified by you in the Create New Class dialog + name of the new interface specified by you in the Create New Class dialog ${USER}   - current user system login name + current user system login name ${DATE}   - current system date + current system date ${TIME}   - current system time + current system time ${YEAR}   - current year + current year ${MONTH}   - current month + current month ${MONTH_NAME_SHORT}   - first 3 letters of the current month name. Example: Jan, Feb, etc. + first 3 letters of the current month name. Example: Jan, Feb, etc. ${MONTH_NAME_FULL}   - full name of the current month. Example: January, February, etc. + full name of the current month. Example: January, February, etc. ${DAY}   - current day of the month + current day of the month ${HOUR}   - current hour + current hour ${MINUTE}   - current minute + current minute ${PROJECT_NAME}   - the name of the current project + the name of the current project diff --git a/resources-en/src/fileTemplates/internal/package-info.java.html b/resources-en/src/fileTemplates/internal/package-info.java.html index 32529e35dc6d..6698e0c822db 100644 --- a/resources-en/src/fileTemplates/internal/package-info.java.html +++ b/resources-en/src/fileTemplates/internal/package-info.java.html @@ -24,62 +24,62 @@ ${PACKAGE_NAME}   - name of the package in which the new package-info.java file is created + name of the package in which the new package-info.java file is created ${USER}   - current user system login name + current user system login name ${DATE}   - current system date + current system date ${TIME}   - current system time + current system time ${YEAR}   - current year + current year ${MONTH}   - current month + current month ${MONTH_NAME_SHORT}   - first 3 letters of the current month name. Example: Jan, Feb, etc. + first 3 letters of the current month name. Example: Jan, Feb, etc. ${MONTH_NAME_FULL}   - full name of the current month. Example: January, February, etc. + full name of the current month. Example: January, February, etc. ${DAY}   - current day of the month + current day of the month ${HOUR}   - current hour + current hour ${MINUTE}   - current minute + current minute ${PROJECT_NAME}   - the name of the current project + the name of the current project From a99d360907baff04abfcccea688072c2f2876988 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Mon, 29 Jun 2015 18:11:16 +0200 Subject: [PATCH 041/137] EA-70172 (CCE: EqualityToEqualsFix.doFix) --- .../src/com/siyeh/ig/fixes/EqualityToEqualsFix.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/EqualityToEqualsFix.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/EqualityToEqualsFix.java index 184ae86ffbbf..e9c4f86eb4ba 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/EqualityToEqualsFix.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/EqualityToEqualsFix.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2015 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,11 +48,11 @@ public class EqualityToEqualsFix extends InspectionGadgetsFix { @Override public void doFix(Project project, ProblemDescriptor descriptor) { final PsiElement comparisonToken = descriptor.getPsiElement(); - final PsiBinaryExpression expression = (PsiBinaryExpression) - comparisonToken.getParent(); - if (expression == null) { + final PsiElement parent = comparisonToken.getParent(); + if (!(parent instanceof PsiBinaryExpression)) { return; } + final PsiBinaryExpression expression = (PsiBinaryExpression)parent; boolean negated = false; final IElementType tokenType = expression.getOperationTokenType(); if (JavaTokenType.NE.equals(tokenType)) { From 63de69e0fe29a9f9b067d3c8ff16dcea7feea453 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Tue, 30 Jun 2015 16:24:15 +0200 Subject: [PATCH 042/137] IDEA-141811 (Cannot Add Files to CVS in Unversioned Files Window) --- .../actions/AddFileOrDirectoryAction.java | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/actions/AddFileOrDirectoryAction.java b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/actions/AddFileOrDirectoryAction.java index 9a9d4f77b3a4..212c6f680d40 100644 --- a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/actions/AddFileOrDirectoryAction.java +++ b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/actions/AddFileOrDirectoryAction.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. @@ -31,6 +31,9 @@ import com.intellij.cvsSupport2.cvsoperations.cvsAdd.ui.AbstractAddOptionsDialog import com.intellij.cvsSupport2.ui.CvsTabbedWindow; import com.intellij.cvsSupport2.ui.Options; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.application.Application; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.actions.VcsContext; @@ -100,14 +103,26 @@ public class AddFileOrDirectoryAction extends ActionOnSelectedElement { LOG.error(filesToAdd); } - if (showDialog) { - final AbstractAddOptionsDialog dialog = AbstractAddOptionsDialog.createDialog(project, roots, dialogOptions); - if (!dialog.showAndGet()) { - return CvsHandler.NULL; + if (!showDialog) { + return CommandCvsHandler.createAddFilesHandler(project, roots); + } + final CvsHandler[] handler = new CvsHandler[1]; + final Runnable runnable = new Runnable() { + @Override + public void run() { + final AbstractAddOptionsDialog dialog = AbstractAddOptionsDialog.createDialog(project, roots, dialogOptions); + handler[0] = !dialog.showAndGet() ? CvsHandler.NULL : CommandCvsHandler.createAddFilesHandler(project, roots); } + }; + final Application application = ApplicationManager.getApplication(); + if (application.isDispatchThread()) { + runnable.run(); + } + else { + application.invokeAndWait(runnable, ModalityState.any()); } - return CommandCvsHandler.createAddFilesHandler(project, roots); + return handler[0]; } @Override From d01aa8187ab7e51884300daf2d6d532b2dc37589 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Wed, 1 Jul 2015 22:04:27 +0200 Subject: [PATCH 043/137] IG: cleanup --- .../ig/psiutils/DefiniteAssignmentUtil.java | 73 +++++++++---------- 1 file changed, 34 insertions(+), 39 deletions(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/DefiniteAssignmentUtil.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/DefiniteAssignmentUtil.java index a5932ffc0ec2..765df624c522 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/DefiniteAssignmentUtil.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/DefiniteAssignmentUtil.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. @@ -17,6 +17,7 @@ package com.siyeh.ig.psiutils; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -29,10 +30,29 @@ import org.jetbrains.annotations.Nullable; public final class DefiniteAssignmentUtil { public static void checkVariable(PsiVariable variable, DefiniteAssignment definiteAssignment) { + if (variable.getInitializer() != null) { + throw new IllegalArgumentException("variable has initializer, check for assignment to the field"); + } if (variable instanceof PsiField) { final PsiField field = (PsiField)variable; checkField(field, definiteAssignment); } + else if (variable instanceof PsiParameter) { + throw new IllegalArgumentException("parameter has implicit initializer, check for assignment to the parameter"); + } + else if (variable instanceof PsiLocalVariable) { + final PsiLocalVariable localVariable = (PsiLocalVariable)variable; + final PsiElement parent = localVariable.getParent(); + assert parent instanceof PsiDeclarationStatement; + PsiStatement statement = (PsiStatement)parent; + while (statement != null) { + checkStatement(statement, definiteAssignment); + statement = PsiTreeUtil.getNextSiblingOfType(statement, PsiStatement.class); + } + } + else { + assert false; + } } private static void checkField(PsiField field, DefiniteAssignment definiteAssignment) { @@ -44,42 +64,22 @@ public final class DefiniteAssignmentUtil { return; } final PsiElement[] children = aClass.getChildren(); - if (field.hasModifierProperty(PsiModifier.STATIC)) { - for (PsiElement child : children) { - if (child instanceof PsiField) { - final PsiField otherField = (PsiField)child; - if (!otherField.hasModifierProperty(PsiModifier.STATIC)) { - continue; - } + final boolean isStatic = field.hasModifierProperty(PsiModifier.STATIC); + for (PsiElement child : children) { + if (child instanceof PsiField) { + final PsiField otherField = (PsiField)child; + if (otherField.hasModifierProperty(PsiModifier.STATIC) == isStatic) { checkExpression(otherField.getInitializer(), definiteAssignment, BooleanExpressionValue.UNDEFINED); } - else if (child instanceof PsiClassInitializer) { - final PsiClassInitializer classInitializer = (PsiClassInitializer)child; - if (!classInitializer.hasModifierProperty(PsiModifier.STATIC)) { - continue; - } + } + else if (child instanceof PsiClassInitializer) { + final PsiClassInitializer classInitializer = (PsiClassInitializer)child; + if (classInitializer.hasModifierProperty(PsiModifier.STATIC) == isStatic) { checkCodeBlock(classInitializer.getBody(), definiteAssignment); } } } - else { - for (PsiElement child : children) { - if (child instanceof PsiField) { - final PsiField otherField = (PsiField)child; - if (otherField.hasModifierProperty(PsiModifier.STATIC)) { - continue; - } - checkExpression(otherField.getInitializer(), definiteAssignment, BooleanExpressionValue.UNDEFINED); - } - else if (child instanceof PsiClassInitializer) { - final PsiClassInitializer classInitializer = (PsiClassInitializer)child; - if (classInitializer.hasModifierProperty(PsiModifier.STATIC)) { - continue; - } - checkCodeBlock(classInitializer.getBody(), definiteAssignment); - } - if (definiteAssignment.stop()) return; - } + if (!isStatic) { final PsiMethod[] constructors = aClass.getConstructors(); if (constructors.length != 0) { // missing from spec? final boolean da = definiteAssignment.isDefinitelyAssigned(); @@ -481,14 +481,9 @@ public final class DefiniteAssignmentUtil { } if (PsiType.BOOLEAN.equals(expression.getType())) { final Object result = ExpressionUtils.computeConstantExpression(expression); - if (Boolean.TRUE == result) { - if (BooleanExpressionValue.WHEN_FALSE == value) { - definiteAssignment.set(true, true); - } - return; - } - else if (Boolean.FALSE == result) { - if (BooleanExpressionValue.WHEN_TRUE == value) { + if (result != null) { + if (Boolean.TRUE == result && BooleanExpressionValue.WHEN_FALSE == value || + Boolean.FALSE == result && BooleanExpressionValue.WHEN_TRUE == value) { definiteAssignment.set(true, true); } return; From 7ea2d6d175c9379d39b4aca2566dfdb741a6dc6f Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Wed, 1 Jul 2015 22:21:31 +0200 Subject: [PATCH 044/137] IG: check fields with initializers correctly in "Field may be final" inspection --- .../src/com/siyeh/ig/psiutils/FinalUtils.java | 6 ++++- .../ig/psiutils/VariableAccessUtils.java | 27 +++++++++++++++++++ .../style/field_final/FieldMayBeFinal.java | 12 +++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FinalUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FinalUtils.java index 1155a1083906..e320e35b1c2f 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FinalUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FinalUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2013 Bas Leijdekkers + * Copyright 2009-2015 Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,10 @@ public class FinalUtils { private FinalUtils() {} public static boolean canBeFinal(@NotNull PsiVariable variable) { + if (variable.getInitializer() != null || variable instanceof PsiParameter) { + // parameters hava implicit initializer + return !VariableAccessUtils.variableIsAssigned(variable); + } final FinalDefiniteAssignment definiteAssignment = new FinalDefiniteAssignment(variable); DefiniteAssignmentUtil.checkVariable(variable, definiteAssignment); return definiteAssignment.isDefinitelyAssigned() && diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/VariableAccessUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/VariableAccessUtils.java index 03bf5937d44f..a6639f51d1e8 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/VariableAccessUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/VariableAccessUtils.java @@ -16,7 +16,10 @@ package com.siyeh.ig.psiutils; import com.intellij.psi.*; +import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -79,6 +82,30 @@ public class VariableAccessUtils { return visitor.isPassed(); } + public static boolean variableIsAssigned(@NotNull PsiVariable variable) { + if (variable instanceof PsiField) { + if (variable.hasModifierProperty(PsiModifier.PRIVATE)) { + final PsiClass aClass = PsiUtil.getTopLevelClass(variable); + return variableIsAssigned(variable, aClass); + } + return !ReferencesSearch.search(variable, variable.getUseScope()).forEach(new Processor() { + @Override + public boolean process(PsiReference reference) { + final PsiElement element = reference.getElement(); + if (!(element instanceof PsiExpression)) { + return true; + } + final PsiExpression expression = (PsiExpression)element; + return !PsiUtil.isAccessedForWriting(expression); + } + }); + } + final PsiElement context = + PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class, PsiMethod.class, PsiLambdaExpression.class, + PsiCatchSection.class, PsiForStatement.class, PsiForeachStatement.class); + return variableIsAssigned(variable, context); + } + public static boolean variableIsAssigned( @NotNull PsiVariable variable, @Nullable PsiElement context) { if (context == null) { diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/field_final/FieldMayBeFinal.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/field_final/FieldMayBeFinal.java index e532626bfd39..299549b239ed 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/field_final/FieldMayBeFinal.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/field_final/FieldMayBeFinal.java @@ -886,6 +886,18 @@ class T58 { x = 3; } } +class T59 { + private int i = 0; + { + assert true : i++; + } +} +class T60 { + private int i = 1; + { + if (false) i = 2; + } +} class Foo { public interface Accessor { From 71aaf4065dd567c9a0e708e7f8609ba1d5de8b5d Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Wed, 1 Jul 2015 22:37:10 +0200 Subject: [PATCH 045/137] IDEA-142052 ("'try finally' replaceable with 'try' with resources" inspection creates non compiling code) --- ...ryFinallyCanBeTryWithResourcesInspection.java | 6 +++++- .../src/com/siyeh/ig/psiutils/FinalUtils.java | 2 +- .../TryFinallyCanBeTryWithResources.java | 16 ++++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java index 3ac50a5fbfe6..2dd21f4b8ed7 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.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. @@ -27,6 +27,7 @@ import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.FinalUtils; import com.siyeh.ig.psiutils.PsiElementOrderComparator; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; @@ -352,6 +353,9 @@ public class TryFinallyCanBeTryWithResourcesInspection extends BaseInspection { } static boolean isVariableUsedOutsideContext(PsiVariable variable, PsiElement context) { + if (!FinalUtils.canBeFinal(variable)) { + return true; + } final VariableUsedOutsideContextVisitor visitor = new VariableUsedOutsideContextVisitor(variable, context); final PsiElement declarationScope = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class); if (declarationScope == null) { diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FinalUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FinalUtils.java index e320e35b1c2f..d51d94ca0f58 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FinalUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FinalUtils.java @@ -25,7 +25,7 @@ public class FinalUtils { public static boolean canBeFinal(@NotNull PsiVariable variable) { if (variable.getInitializer() != null || variable instanceof PsiParameter) { - // parameters hava implicit initializer + // parameters have an implicit initializer return !VariableAccessUtils.variableIsAssigned(variable); } final FinalDefiniteAssignment definiteAssignment = new FinalDefiniteAssignment(variable); diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/migration/try_finally_can_be_try_with_resources/TryFinallyCanBeTryWithResources.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/migration/try_finally_can_be_try_with_resources/TryFinallyCanBeTryWithResources.java index ab34d8d4c15f..83cd11796df7 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/migration/try_finally_can_be_try_with_resources/TryFinallyCanBeTryWithResources.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/migration/try_finally_can_be_try_with_resources/TryFinallyCanBeTryWithResources.java @@ -1,5 +1,6 @@ package com.siyeh.igtest.migration.try_finally_can_be_try_with_resources; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; @@ -33,4 +34,19 @@ class TryFinallyCanBeTryWithResources { stream.close(); } } + + public void write3() throws IOException { + InputStream in = true ? new FileInputStream("null") : null; + try { + byte[] magicNumber = new byte[2]; + in.mark(2); + in.read(magicNumber); + in.reset(); + if (false) { + in = new FileInputStream("in"); // var can't be (implicitly final) resource var, because it is reassigned here + } + } finally { + in.close(); + } + } } \ No newline at end of file From bac27e2d1322002ed30ab1c5e57b62b6ba0082e0 Mon Sep 17 00:00:00 2001 From: Nadya Zabrodina Date: Thu, 25 Jun 2015 17:20:34 +0400 Subject: [PATCH 046/137] IDEA-44866 Shelve Changes: Cancel should work * Disable cancel button when non cancelable section starts in ProgressWindow --- .../progress/impl/ProgressManagerImpl.java | 6 +++--- .../openapi/progress/util/ProgressDialog.java | 11 +++++++--- .../openapi/progress/util/ProgressWindow.java | 20 +++++++++++++++++++ .../src/messages/VcsBundle.properties | 1 + .../changes/shelf/ShelveChangesManager.java | 10 +++++++++- .../changes/ui/CommitChangeListDialog.java | 6 +++--- 6 files changed, 44 insertions(+), 10 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/progress/impl/ProgressManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/progress/impl/ProgressManagerImpl.java index b10d4a2ddc60..59cb98f268b2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/progress/impl/ProgressManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/progress/impl/ProgressManagerImpl.java @@ -108,9 +108,9 @@ public class ProgressManagerImpl extends CoreProgressManager implements Disposab @Override @NotNull public Future runProcessWithProgressAsynchronously(@NotNull final Task.Backgroundable task, - @NotNull final ProgressIndicator progressIndicator, - @Nullable final Runnable continuation, - @NotNull final ModalityState modalityState) { + @NotNull final ProgressIndicator progressIndicator, + @Nullable final Runnable continuation, + @NotNull final ModalityState modalityState) { if (progressIndicator instanceof Disposable) { Disposer.register(ApplicationManager.getApplication(), (Disposable)progressIndicator); } diff --git a/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressDialog.java b/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressDialog.java index 1af8c87d2444..ac3cd0066991 100644 --- a/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressDialog.java +++ b/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressDialog.java @@ -17,6 +17,7 @@ package com.intellij.openapi.progress.util; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.DialogWrapperPeer; @@ -221,13 +222,17 @@ class ProgressDialog implements Disposable { } void cancel() { + enableCancelButtonIfNeeded(false); + } + + void enableCancelButtonIfNeeded(final boolean enable) { if (myProgressWindow.myShouldShowCancel) { - SwingUtilities.invokeLater(new Runnable() { + ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { - myCancelButton.setEnabled(false); + myCancelButton.setEnabled(enable); } - }); + }, ModalityState.any()); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressWindow.java b/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressWindow.java index 4cdbb8b60242..9487ec1cce9a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressWindow.java +++ b/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressWindow.java @@ -241,6 +241,22 @@ public class ProgressWindow extends ProgressIndicatorBase implements BlockingPro } } + @Override + public void startNonCancelableSection() { + if (isCancelable()) { + enableCancel(false); + } + super.startNonCancelableSection(); + } + + @Override + public void finishNonCancelableSection() { + super.finishNonCancelableSection(); + if (isCancelable()) { + enableCancel(true); + } + } + @Override public void setIndeterminate(boolean indeterminate) { super.setIndeterminate(indeterminate); @@ -384,4 +400,8 @@ public class ProgressWindow extends ProgressIndicatorBase implements BlockingPro public boolean isPopupWasShown() { return myDialog != null && myDialog.myPopup != null && myDialog.myPopup.isShowing(); } + + protected void enableCancel(boolean enable) { + myDialog.enableCancelButtonIfNeeded(enable); + } } diff --git a/platform/platform-resources-en/src/messages/VcsBundle.properties b/platform/platform-resources-en/src/messages/VcsBundle.properties index 495e33efca33..3346f02c164d 100644 --- a/platform/platform-resources-en/src/messages/VcsBundle.properties +++ b/platform/platform-resources-en/src/messages/VcsBundle.properties @@ -352,6 +352,7 @@ patch.apply.conflict.patched.version=Patched Version patch.apply.select.title=Select Patch File patch.apply.select.base.directory.title=Select Base Directory shelve.changes.action=Shelve Changes +shelve.changes.progress.title=Shelving Changes... patch.apply.already.applied=All of the changes in the specified patch are already contained in the code patch.apply.partially.applied=Some of the changes in the specified patch were skipped because they are already contained in the code patch.apply.success.applied.text=Patch successfully applied diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java index fc14371c55b5..bf934fa68569 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java @@ -34,6 +34,7 @@ import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchBase; import com.intellij.openapi.diff.impl.patch.formove.CustomBinaryPatchApplier; import com.intellij.openapi.diff.impl.patch.formove.PatchApplier; import com.intellij.openapi.progress.AsynchronousExecution; +import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ex.ProjectEx; @@ -146,6 +147,10 @@ public class ShelveChangesManager extends AbstractProjectComponent implements JD public ShelvedChangeList shelveChanges(final Collection changes, final String commitMessage, final boolean rollback) throws IOException, VcsException { + final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator(); + if (progressIndicator != null) { + progressIndicator.setText(VcsBundle.message("shelve.changes.progress.title")); + } final List textChanges = new ArrayList(); final List binaryFiles = new ArrayList(); for (Change change : changes) { @@ -180,12 +185,15 @@ public class ShelveChangesManager extends AbstractProjectComponent implements JD patchPath, commitContext); changeList = new ShelvedChangeList(patchPath.toString(), commitMessage.replace('\n', ' '), binaryFiles); - myShelvedChangeLists.add(changeList); ProgressManager.checkCanceled(); + myShelvedChangeLists.add(changeList); if (rollback) { final String operationName = UIUtil.removeMnemonic(RollbackChangesDialog.operationNameByChanges(myProject, changes)); boolean modalContext = ApplicationManager.getApplication().isDispatchThread() && LaterInvocator.isInModalContext(); + if (progressIndicator != null) { + progressIndicator.startNonCancelableSection(); + } new RollbackWorker(myProject, operationName, modalContext). doRollback(changes, true, null, VcsBundle.message("shelve.changes.action")); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java index 3125ab0d34e4..d83564e213ed 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java @@ -24,6 +24,7 @@ import com.intellij.openapi.actionSystem.DataSink; import com.intellij.openapi.actionSystem.TypeSafeDataProvider; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.progress.ProgressManager; @@ -68,6 +69,7 @@ import java.util.List; public class CommitChangeListDialog extends DialogWrapper implements CheckinProjectPanel, TypeSafeDataProvider { private final static String outCommitHelpId = "reference.dialogs.vcs.commit"; private static final int LAYOUT_VERSION = 2; + private static final Logger LOG = Logger.getInstance(CommitChangeListDialog.class); private final CommitContext myCommitContext; private final CommitMessage myCommitMessageArea; private Splitter mySplitter; @@ -692,7 +694,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj commitExecutor.getActionText()); for (CheckinHandler handler : myHandlers) { - handler.checkinFailed(Arrays.asList(new VcsException(e))); + handler.checkinFailed(Collections.singletonList(new VcsException(e))); } } finally { @@ -707,8 +709,6 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj } } }, commitExecutor); - - } else { session.executionCanceled(); From dab52f5ff80e48c32ba91b81931994667f2f2101 Mon Sep 17 00:00:00 2001 From: Nadya Zabrodina Date: Wed, 24 Jun 2015 18:48:00 +0400 Subject: [PATCH 047/137] Create patch additional component panel optimized * unused dvcsUsed variable and appropriate for-statement removed; * cache additionalUIPanel instead of double creation; * new patch dialog creation become 2 times faster when called from commitChanges dialog --- .../patch/CreatePatchCommitExecutor.java | 39 +++++++------------ .../patch/CreatePatchConfigurationPanel.java | 4 +- .../changes/ui/CommitChangeListDialog.java | 5 ++- .../openapi/vcs/changes/ui/SessionDialog.java | 11 +++++- 4 files changed, 28 insertions(+), 31 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchCommitExecutor.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchCommitExecutor.java index 4e2d89ac752c..7c9a1bfaf5f9 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchCommitExecutor.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchCommitExecutor.java @@ -29,14 +29,15 @@ import com.intellij.openapi.diff.impl.patch.IdeaTextPatchBuilder; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.DefaultJDOMExternalizer; -import com.intellij.openapi.util.InvalidDataException; -import com.intellij.openapi.util.JDOMExternalizable; -import com.intellij.openapi.util.WriteExternalException; -import com.intellij.openapi.vcs.*; +import com.intellij.openapi.util.*; +import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.VcsApplicationSettings; +import com.intellij.openapi.vcs.VcsBundle; +import com.intellij.openapi.vcs.VcsConfiguration; import com.intellij.openapi.vcs.changes.*; import com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager; import com.intellij.util.WaitForProgressToShow; +import com.intellij.util.containers.ContainerUtil; import org.jdom.Element; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; @@ -54,7 +55,7 @@ import java.util.List; */ public class CreatePatchCommitExecutor extends LocalCommitExecutor implements ProjectComponent, JDOMExternalizable { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.patch.CreatePatchCommitExecutor"); - + private final Project myProject; private final ChangeListManager myChangeListManager; @@ -139,24 +140,13 @@ public class CreatePatchCommitExecutor extends LocalCommitExecutor implements Pr myPanel.setFileName(ShelveChangesManager.suggestPatchName(myProject, commitMessage, new File(PATCH_PATH), null)); myPanel.setReversePatch(false); - boolean dvcsIsUsed = false; - - if (ProjectLevelVcsManager.getInstance(myProject).dvcsUsedInProject()) { - for (Change change : changes) { - final AbstractVcs vcs = ChangesUtil.getVcsForChange(change, myProject); - if (vcs != null && VcsType.distributed.equals(vcs.getType())) { - dvcsIsUsed = true; - break; - } + myPanel.setChanges(ContainerUtil.filter(changes, new Condition() { + @Override + public boolean value(Change change) { + return change.getBeforeRevision() != null && change.getAfterRevision() != null; } - } - final List modified = new ArrayList(); - for (Change change : changes) { - if (change.getBeforeRevision() == null || change.getAfterRevision() == null) continue; - modified.add(change); - } - myPanel.setChanges(modified); - myPanel.showTextStoreOption(dvcsIsUsed); + })); + myPanel.showTextStoreOption(); return myPanel.getPanel(); } @@ -258,7 +248,8 @@ public class CreatePatchCommitExecutor extends LocalCommitExecutor implements Pr LOG.info(ex); WaitForProgressToShow.runOrInvokeLaterAboveProgress(new Runnable() { public void run() { - Messages.showErrorDialog(myProject, VcsBundle.message("create.patch.error.title", ex.getMessage()), CommonBundle.getErrorTitle()); + Messages.showErrorDialog(myProject, VcsBundle.message("create.patch.error.title", ex.getMessage()), + CommonBundle.getErrorTitle()); } }, null, myProject); } 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 c398b5fbd62c..131d7e707661 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 @@ -66,7 +66,6 @@ public class CreatePatchConfigurationPanel { private JCheckBox myIncludeBaseRevisionTextCheckBox; private Consumer myOkEnabledListener; private final Project myProject; - private boolean myDvcsIsUsed; private List myChanges; private Collection myIncludedChanges; private SelectFilesToAddTextsToPatchPanel mySelectFilesToAddTextsToPatchPanel; @@ -173,8 +172,7 @@ public class CreatePatchConfigurationPanel { myPanelWithSelectedFiles.add(myHideableTitledPanel, BorderLayout.CENTER); } - public void showTextStoreOption(final boolean dvcsIsUsed) { - myDvcsIsUsed = dvcsIsUsed; + public void showTextStoreOption() { if (myChanges.size() > 0) { myIncludeBaseRevisionTextCheckBox.setVisible(true); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java index d83564e213ed..759a97426dad 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java @@ -653,12 +653,13 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj return; } boolean isOK = true; - if (SessionDialog.createConfigurationUI(session, getIncludedChanges(), getCommitMessage())!= null) { + final JComponent configurationUI = SessionDialog.createConfigurationUI(session, getIncludedChanges(), getCommitMessage()); + if (configurationUI != null) { DialogWrapper sessionDialog = new SessionDialog(commitExecutor.getActionText(), getProject(), session, getIncludedChanges(), - getCommitMessage()); + getCommitMessage(), configurationUI); isOK = sessionDialog.showAndGet(); } if (isOK) { 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 fccbcf69af90..42a336e80999 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 @@ -41,17 +41,24 @@ public class SessionDialog extends DialogWrapper { public SessionDialog(String title, Project project, CommitSession session, List changes, - String commitMessage) { + String commitMessage, @Nullable JComponent configurationComponent) { super(project, true); mySession = session; myChanges = changes; myCommitMessage = commitMessage; - myConfigurationComponent = createConfigurationUI(mySession, myChanges, myCommitMessage); + myConfigurationComponent = + configurationComponent == null ? createConfigurationUI(mySession, myChanges, myCommitMessage) : configurationComponent; setTitle(CommitChangeListDialog.trimEllipsis(title)); init(); updateButtons(); } + public SessionDialog(String title, Project project, + CommitSession session, List changes, + String commitMessage) { + this(title, project, session, changes, commitMessage, null); + } + public static JComponent createConfigurationUI(final CommitSession session, final List changes, final String commitMessage) { try { return session.getAdditionalConfigurationUI(changes, commitMessage); From 305f7ae78f841cd41f982597fe86ce72ddc27613 Mon Sep 17 00:00:00 2001 From: "Vladimir.Orlov" Date: Thu, 2 Jul 2015 08:12:32 +0300 Subject: [PATCH 048/137] fixed exec permission for binaries into unix jre bundled. --- build/scripts/dist.gant | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/scripts/dist.gant b/build/scripts/dist.gant index 373b1e6512aa..dfe40cd92382 100644 --- a/build/scripts/dist.gant +++ b/build/scripts/dist.gant @@ -172,7 +172,7 @@ def layoutAll(Map args, String home, String out, Paths _paths = null, buildJps = buildTarGz("idea-${args.buildNumber}", "$paths.artifacts/idea${args.buildNumber}.tar", [paths.distAll, paths.distUnix]) if (p("jdk.bundled.linux") != "false") { - buildTarGz("idea-${args.buildNumber}", "$paths.artifacts/idea${args.buildNumber}-jdk-bundled.tar", [paths.distAll, paths.distUnix, "${paths.sandbox}/bundled.linux.jdk"], ["jre/bin/*"]) + buildTarGz("idea-${args.buildNumber}", "$paths.artifacts/idea${args.buildNumber}-jdk-bundled.tar", [paths.distAll, paths.distUnix, "${paths.sandbox}/bundled.linux.jdk"], ["jre/jre/bin/*"]) } return info } From 259830af4deb27d16f8fe86bf14bed205218a2f4 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Thu, 2 Jul 2015 11:29:52 +0200 Subject: [PATCH 049/137] IDEA-140785 Replace check getFacets().size() > 0 with hasFacet() which stops on first match --- .../com/intellij/appengine/actions/UploadApplicationAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/google-app-engine/source/com/intellij/appengine/actions/UploadApplicationAction.java b/plugins/google-app-engine/source/com/intellij/appengine/actions/UploadApplicationAction.java index 2ee134dda4b7..028b155a4113 100644 --- a/plugins/google-app-engine/source/com/intellij/appengine/actions/UploadApplicationAction.java +++ b/plugins/google-app-engine/source/com/intellij/appengine/actions/UploadApplicationAction.java @@ -46,7 +46,7 @@ public class UploadApplicationAction extends AnAction { public void update(AnActionEvent e) { final Project project = e.getProject(); e.getPresentation().setEnabledAndVisible( - project != null && !ProjectFacetManager.getInstance(project).getFacets(AppEngineFacet.ID).isEmpty()); + project != null && ProjectFacetManager.getInstance(project).hasFacets(AppEngineFacet.ID)); if (project != null) { String text; From 5e9bd9e7df575d62c20adc7d4e6100f00e74c833 Mon Sep 17 00:00:00 2001 From: Andrey Vokin Date: Thu, 2 Jul 2015 12:39:39 +0300 Subject: [PATCH 050/137] Cucumber JVM. Removed unnecessary bundling of cucumber-java.jar --- build/scripts/libLicenses.gant | 1 - 1 file changed, 1 deletion(-) diff --git a/build/scripts/libLicenses.gant b/build/scripts/libLicenses.gant index e301961d8804..783c833133bc 100644 --- a/build/scripts/libLicenses.gant +++ b/build/scripts/libLicenses.gant @@ -260,7 +260,6 @@ libraryLicense(name: "protobuf", version: "2.5.0", license: "New BSD", url: "htt libraryLicense(name: "Netty", libraryName: "Netty", version: "4.1.0.Beta3", license: "Apache 2.0", url: "http://netty.io", licenseUrl: "http://www.apache.org/licenses/LICENSE-2.0") libraryLicense(name: "Kryo", libraryName: "Kryo", version: "2.22", license: "New BSD License", url: "https://github.com/EsotericSoftware/kryo", licenseUrl: "https://github.com/EsotericSoftware/kryo/blob/master/license.txt") libraryLicense(name: "Snappy-Java", libraryName: "Snappy-Java", version: "0.3.1", license: "Apache 2.0", url: "https://github.com/dain/snappy", licenseUrl: "http://www.apache.org/licenses/LICENSE-2.0") -libraryLicense(name: "Cucumber-Java", libraryName: "cucumber-java", version: "1.2.2", license: "MIT License", url: "https://github.com/cucumber/cucumber-jvm/", licenseUrl: "http://www.opensource.org/licenses/mit-license.html") libraryLicense(name: "Cucumber-JVM", libraryName: "cucumber-jvm", version: "1.2.2", license: "MIT License", url: "https://github.com/cucumber/cucumber-jvm/", licenseUrl: "http://www.opensource.org/licenses/mit-license.html") libraryLicense(name: "Cucumber-Groovy", libraryName: "cucumber-groovy", version: "1.2.2", license: "MIT License", url: "https://github.com/cucumber/cucumber-jvm/", licenseUrl: "http://www.opensource.org/licenses/mit-license.html") libraryLicense(name: "XStream", libraryName: "XStream", version: "1.4.2", license: "BSD License", url: "https://github.com/cucumber/cucumber-jvm-deps/", licenseUrl: "http://xstream.codehaus.org/license.html") From 3e7cafec3c5f6c86e73c60b64aa9e8d8680030a3 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 1 Jul 2015 17:46:52 +0300 Subject: [PATCH 051/137] cleanup --- .../openapi/vfs/newvfs/impl/FileNameCache.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/FileNameCache.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/FileNameCache.java index 7fee33451002..2c49ecd05d5f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/FileNameCache.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/FileNameCache.java @@ -60,13 +60,13 @@ public class FileNameCache { private static int calcStripeIdFromNameId(int id) { int h = id; - h -= (h<<6); - h ^= (h>>17); - h -= (h<<9); - h ^= (h<<4); - h -= (h<<3); - h ^= (h<<10); - h ^= (h>>15); + h -= h<<6; + h ^= h>>17; + h -= h<<9; + h ^= h<<4; + h -= h<<3; + h ^= h<<10; + h ^= h>>15; return h % ourNameCache.length; } From 784d53709b3fb7ebdb741a429fa029c604b1a1f9 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 1 Jul 2015 20:00:21 +0300 Subject: [PATCH 052/137] notnull --- .../core-api/src/com/intellij/lang/PsiBuilder.java | 12 +++++++----- .../core-api/src/com/intellij/lang/PsiParser.java | 4 ++-- .../intellij/lang/WhitespacesAndCommentsBinder.java | 4 +++- .../src/com/intellij/lang/impl/DelegateMarker.java | 13 +++++++------ .../com/intellij/lang/impl/PsiBuilderAdapter.java | 1 + .../intellij/codeInsight/daemon/NavigateAction.java | 12 +++++++----- .../embedding/MasqueradingPsiBuilderAdapter.java | 5 +++-- 7 files changed, 30 insertions(+), 21 deletions(-) diff --git a/platform/core-api/src/com/intellij/lang/PsiBuilder.java b/platform/core-api/src/com/intellij/lang/PsiBuilder.java index bc6d2eca1c4b..66b4abd44afd 100644 --- a/platform/core-api/src/com/intellij/lang/PsiBuilder.java +++ b/platform/core-api/src/com/intellij/lang/PsiBuilder.java @@ -140,6 +140,7 @@ public interface PsiBuilder extends UserDataHolder, UserDataHolderUnprotected { * * @return the new marker instance. */ + @NotNull Marker precede(); /** @@ -160,7 +161,7 @@ public interface PsiBuilder extends UserDataHolder, UserDataHolderUnprotected { * * @param type the type of the node in the AST tree. */ - void done(IElementType type); + void done(@NotNull IElementType type); /** * Like {@linkplain #done(IElementType)}, but collapses all tokens between start and end markers @@ -168,7 +169,7 @@ public interface PsiBuilder extends UserDataHolder, UserDataHolderUnprotected { * * @param type the type of the node in the AST tree. */ - void collapse(IElementType type); + void collapse(@NotNull IElementType type); /** * Like {@linkplain #done(IElementType)}, but the marker is completed (end marker inserted) @@ -178,7 +179,7 @@ public interface PsiBuilder extends UserDataHolder, UserDataHolderUnprotected { * @param type the type of the node in the AST tree. * @param before marker to complete this one before. */ - void doneBefore(IElementType type, Marker before); + void doneBefore(@NotNull IElementType type, @NotNull Marker before); /** * Like {@linkplain #doneBefore(IElementType, Marker)}, but in addition an error element with given text @@ -188,7 +189,7 @@ public interface PsiBuilder extends UserDataHolder, UserDataHolderUnprotected { * @param before marker to complete this one before. * @param errorMessage for error element. */ - void doneBefore(IElementType type, Marker before, String errorMessage); + void doneBefore(@NotNull IElementType type, @NotNull Marker before, String errorMessage); /** * Completes this marker and labels it as error element with specified message. Before calling this method, @@ -204,7 +205,7 @@ public interface PsiBuilder extends UserDataHolder, UserDataHolderUnprotected { * @param message for error element. * @param before marker to complete this one before. */ - void errorBefore(String message, Marker before); + void errorBefore(String message, @NotNull Marker before); /** * Allows to define custom edge token binders instead of default ones. If any of parameters is null @@ -222,6 +223,7 @@ public interface PsiBuilder extends UserDataHolder, UserDataHolderUnprotected { * * @return the new marker instance. */ + @NotNull Marker mark(); /** diff --git a/platform/core-api/src/com/intellij/lang/PsiParser.java b/platform/core-api/src/com/intellij/lang/PsiParser.java index 0d241bcb50f1..0962ed977ed4 100644 --- a/platform/core-api/src/com/intellij/lang/PsiParser.java +++ b/platform/core-api/src/com/intellij/lang/PsiParser.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. @@ -38,5 +38,5 @@ public interface PsiParser { * @return the root of the resulting AST tree. */ @NotNull - ASTNode parse(IElementType root, PsiBuilder builder); + ASTNode parse(@NotNull IElementType root, @NotNull PsiBuilder builder); } diff --git a/platform/core-api/src/com/intellij/lang/WhitespacesAndCommentsBinder.java b/platform/core-api/src/com/intellij/lang/WhitespacesAndCommentsBinder.java index 2c24103f954b..7c3eb4070d63 100644 --- a/platform/core-api/src/com/intellij/lang/WhitespacesAndCommentsBinder.java +++ b/platform/core-api/src/com/intellij/lang/WhitespacesAndCommentsBinder.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2010 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. @@ -16,6 +16,7 @@ package com.intellij.lang; import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.NotNull; import java.util.List; @@ -31,6 +32,7 @@ public interface WhitespacesAndCommentsBinder { * Provides an ability for the processor to get a text of any of given tokens. */ interface TokenTextGetter { + @NotNull CharSequence get(int i); } diff --git a/platform/core-impl/src/com/intellij/lang/impl/DelegateMarker.java b/platform/core-impl/src/com/intellij/lang/impl/DelegateMarker.java index c28af8b4860b..10b6ae5f9d7d 100644 --- a/platform/core-impl/src/com/intellij/lang/impl/DelegateMarker.java +++ b/platform/core-impl/src/com/intellij/lang/impl/DelegateMarker.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. @@ -35,6 +35,7 @@ public abstract class DelegateMarker implements PsiBuilder.Marker { return myDelegate; } + @NotNull @Override public PsiBuilder.Marker precede() { return myDelegate.precede(); @@ -51,22 +52,22 @@ public abstract class DelegateMarker implements PsiBuilder.Marker { } @Override - public void done(IElementType type) { + public void done(@NotNull IElementType type) { myDelegate.done(type); } @Override - public void collapse(IElementType type) { + public void collapse(@NotNull IElementType type) { myDelegate.collapse(type); } @Override - public void doneBefore(IElementType type, PsiBuilder.Marker before) { + public void doneBefore(@NotNull IElementType type, @NotNull PsiBuilder.Marker before) { myDelegate.doneBefore(type, before); } @Override - public void doneBefore(IElementType type, PsiBuilder.Marker before, String errorMessage) { + public void doneBefore(@NotNull IElementType type, @NotNull PsiBuilder.Marker before, String errorMessage) { myDelegate.doneBefore(type, before, errorMessage); } @@ -76,7 +77,7 @@ public abstract class DelegateMarker implements PsiBuilder.Marker { } @Override - public void errorBefore(String message, PsiBuilder.Marker before) { + public void errorBefore(String message, @NotNull PsiBuilder.Marker before) { myDelegate.errorBefore(message, before); } diff --git a/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderAdapter.java b/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderAdapter.java index b66f8062d42e..9cd3540180b0 100644 --- a/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderAdapter.java +++ b/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderAdapter.java @@ -101,6 +101,7 @@ public class PsiBuilderAdapter implements PsiBuilder { return myDelegate.getCurrentOffset(); } + @NotNull @Override public Marker mark() { return myDelegate.mark(); diff --git a/platform/lang-api/src/com/intellij/codeInsight/daemon/NavigateAction.java b/platform/lang-api/src/com/intellij/codeInsight/daemon/NavigateAction.java index 90ccfbffbf8b..bc3bb1de1bd5 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/daemon/NavigateAction.java +++ b/platform/lang-api/src/com/intellij/codeInsight/daemon/NavigateAction.java @@ -20,6 +20,7 @@ import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.ShortcutSet; import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.awt.event.MouseEvent; @@ -28,10 +29,10 @@ import java.awt.event.MouseEvent; * @author Dmitry Avdeev */ public class NavigateAction extends AnAction { - private LineMarkerInfo myInfo; + private final LineMarkerInfo myInfo; - public NavigateAction(String text, - LineMarkerInfo info, + public NavigateAction(@NotNull String text, + @NotNull LineMarkerInfo info, @Nullable String originalActionId) { super(text); myInfo = info; @@ -41,7 +42,7 @@ public class NavigateAction extends AnAction { } } - public NavigateAction(LineMarkerInfo info) { + public NavigateAction(@NotNull LineMarkerInfo info) { myInfo = info; } @@ -56,7 +57,8 @@ public class NavigateAction extends AnAction { } } - public static LineMarkerInfo setNavigateAction(LineMarkerInfo info, String text, @Nullable String originalActionId) { + @NotNull + public static LineMarkerInfo setNavigateAction(@NotNull LineMarkerInfo info, @NotNull String text, @Nullable String originalActionId) { NavigateAction action = new NavigateAction(text, info, originalActionId); info.setNavigateAction(action); return info; diff --git a/xml/xml-psi-impl/src/com/intellij/embedding/MasqueradingPsiBuilderAdapter.java b/xml/xml-psi-impl/src/com/intellij/embedding/MasqueradingPsiBuilderAdapter.java index 2aef6155b8ef..4f3488517c7b 100644 --- a/xml/xml-psi-impl/src/com/intellij/embedding/MasqueradingPsiBuilderAdapter.java +++ b/xml/xml-psi-impl/src/com/intellij/embedding/MasqueradingPsiBuilderAdapter.java @@ -222,6 +222,7 @@ public class MasqueradingPsiBuilderAdapter extends PsiBuilderAdapter { return true; } + @NotNull @Override public Marker mark() { // In the case of the topmost node all should be inserted @@ -344,12 +345,12 @@ public class MasqueradingPsiBuilderAdapter extends PsiBuilderAdapter { } @Override - public void doneBefore(IElementType type, Marker before) { + public void doneBefore(@NotNull IElementType type, @NotNull Marker before) { super.doneBefore(type, getDelegateOrThis(before)); } @Override - public void doneBefore(IElementType type, Marker before, String errorMessage) { + public void doneBefore(@NotNull IElementType type, @NotNull Marker before, String errorMessage) { super.doneBefore(type, getDelegateOrThis(before), errorMessage); } From 863eeb189854e46b1bce926cacce1656406a52ac Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 1 Jul 2015 20:21:49 +0300 Subject: [PATCH 053/137] IDEA-141330 Find Usages cannot find valid usage from abstract class --- .../daemon/impl/UnusedSymbolUtil.java | 18 +-- .../psi/impl/FindSuperElementsHelper.java | 125 ++++++++++++++++++ .../daemon/impl/JavaLineMarkerProvider.java | 122 +++++++++++------ .../codeInsight/daemon/impl/MarkerType.java | 46 ++++--- .../hint/actions/ShowSiblingsAction.java | 20 ++- .../navigation/JavaGotoSuperHandler.java | 38 +++--- .../ide/util/SuperMethodWarningUtil.java | 33 +++-- .../JavaAllOverridingMethodsSearcher.java | 4 +- .../psi/impl/FindSuperElementsHelper.java | 56 -------- .../gotosuper/SiblingInheritance.after.java | 11 ++ .../gotosuper/SiblingInheritance.java | 11 ++ .../impl}/JavaGotoSuperTest.java | 72 ++++++++-- ...odNotExposedInInterfaceInspectionBase.java | 14 +- .../NoopMethodInAbstractClassInspection.java | 5 + .../codeInsight/GroovyLineMarkerProvider.java | 23 ++-- 15 files changed, 402 insertions(+), 196 deletions(-) create mode 100644 java/java-analysis-impl/src/com/intellij/psi/impl/FindSuperElementsHelper.java delete mode 100644 java/java-psi-impl/src/com/intellij/psi/impl/FindSuperElementsHelper.java create mode 100644 java/java-tests/testData/codeInsight/gotosuper/SiblingInheritance.after.java create mode 100644 java/java-tests/testData/codeInsight/gotosuper/SiblingInheritance.java rename java/java-tests/testSrc/com/intellij/codeInsight/{navigation => daemon/impl}/JavaGotoSuperTest.java (51%) diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/UnusedSymbolUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/UnusedSymbolUtil.java index a200ebd04f89..b1c858b0d9bb 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/UnusedSymbolUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/UnusedSymbolUtil.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. @@ -26,6 +26,7 @@ import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; import com.intellij.psi.*; +import com.intellij.psi.impl.FindSuperElementsHelper; import com.intellij.psi.impl.source.PsiClassImpl; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.PsiSearchHelper; @@ -135,14 +136,15 @@ public class UnusedSymbolUtil { } else { //class maybe used in some weird way, e.g. from XML, therefore the only constructor is used too - if (containingClass != null && method.isConstructor() + boolean isConstructor = method.isConstructor(); + if (containingClass != null && isConstructor && containingClass.getConstructors().length == 1 && isClassUsed(project, containingFile, containingClass, progress, helper)) { return true; } if (isImplicitUsage(project, method, progress)) return true; - if (method.findSuperMethods().length != 0) { + if (!isConstructor && FindSuperElementsHelper.findSuperElements(method).length != 0) { return true; } if (!weAreSureThereAreNoUsages(project, containingFile, method, progress, helper)) { @@ -193,7 +195,7 @@ public class UnusedSymbolUtil { @NotNull PsiFile containingFile, @NotNull PsiMember member, @NotNull ProgressIndicator progress, - final PsiFile ignoreFile, + @Nullable PsiFile ignoreFile, @NotNull Processor usageInfoProcessor) { String name = member.getName(); if (name == null) { @@ -247,10 +249,8 @@ public class UnusedSymbolUtil { } else if (member instanceof PsiMethod) { PsiMethod method = (PsiMethod)member; - JavaMethodFindUsagesOptions o = new JavaMethodFindUsagesOptions(project); - //o.isIncludeOverloadUsages = true; - options = o; - options.isSearchForTextOccurrences = method.isConstructor();; + options = new JavaMethodFindUsagesOptions(project); + options.isSearchForTextOccurrences = method.isConstructor(); } else if (member instanceof PsiVariable) { options = new JavaVariableFindUsagesOptions(project); @@ -271,7 +271,7 @@ public class UnusedSymbolUtil { @NotNull ProgressIndicator progress, @NotNull GlobalUsageHelper helper) { final PsiClass containingClass = member.getContainingClass(); - if (containingClass == null || !(containingClass instanceof PsiClassImpl)) return true; + if (!(containingClass instanceof PsiClassImpl)) return true; final PsiMethod valuesMethod = ((PsiClassImpl)containingClass).getValuesMethod(); return valuesMethod == null || isMethodReferenced(project, containingFile, valuesMethod, progress, helper); } diff --git a/java/java-analysis-impl/src/com/intellij/psi/impl/FindSuperElementsHelper.java b/java/java-analysis-impl/src/com/intellij/psi/impl/FindSuperElementsHelper.java new file mode 100644 index 000000000000..f52bffd65b4c --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/psi/impl/FindSuperElementsHelper.java @@ -0,0 +1,125 @@ +/* + * 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.searches.ClassInheritorsSearch; +import com.intellij.psi.util.MethodSignature; +import com.intellij.psi.util.MethodSignatureUtil; +import com.intellij.psi.util.PsiSuperMethodUtil; +import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.util.Processor; +import com.intellij.util.containers.FactoryMap; +import gnu.trove.THashSet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public class FindSuperElementsHelper { + @NotNull + public static PsiElement[] findSuperElements(@NotNull PsiElement element) { + if (element instanceof PsiClass) { + PsiClass aClass = (PsiClass) element; + List allSupers = new ArrayList(Arrays.asList(aClass.getSupers())); + for (Iterator iterator = allSupers.iterator(); iterator.hasNext();) { + PsiClass superClass = iterator.next(); + if (CommonClassNames.JAVA_LANG_OBJECT.equals(superClass.getQualifiedName())) iterator.remove(); + } + return allSupers.toArray(new PsiClass[allSupers.size()]); + } + if (element instanceof PsiMethod) { + PsiMethod method = (PsiMethod) element; + if (method.isConstructor()) { + PsiMethod constructorInSuper = PsiSuperMethodUtil.findConstructorInSuper(method); + if (constructorInSuper != null) { + return new PsiMethod[]{constructorInSuper}; + } + } + else { + PsiMethod[] superMethods = method.findSuperMethods(false); + if (superMethods.length == 0) { + PsiMethod superMethod = getSiblingInheritedViaSubClass(method); + if (superMethod != null) { + superMethods = new PsiMethod[]{superMethod}; + } + } + return superMethods; + } + } + return PsiElement.EMPTY_ARRAY; + } + + public static PsiMethod getSiblingInheritedViaSubClass(@NotNull PsiMethod method) { + return getSiblingInheritedViaSubClass(method, createSubClassCache()); + } + + public static PsiMethod getSiblingInheritedViaSubClass(@NotNull final PsiMethod method, + @NotNull Map subClassCache) { + if (!method.hasModifierProperty(PsiModifier.PUBLIC)) return null; + if (method.hasModifierProperty(PsiModifier.STATIC)) return null; + final PsiClass containingClass = method.getContainingClass(); + boolean hasSubClass = containingClass != null && !containingClass.isInterface() && subClassCache.get(containingClass) != null; + if (!hasSubClass) { + return null; + } + final Collection checkedInterfaces = new THashSet(); + final PsiMethod[] result = new PsiMethod[1]; + ClassInheritorsSearch.search(containingClass, true).forEach(new Processor() { + @Override + public boolean process(PsiClass inheritor) { + for (PsiClassType interfaceType : inheritor.getImplementsListTypes()) { + PsiClassType.ClassResolveResult resolved = interfaceType.resolveGenerics(); + PsiClass anInterface = resolved.getElement(); + if (anInterface == null || !checkedInterfaces.add(anInterface)) continue; + for (PsiMethod superMethod : anInterface.findMethodsByName(method.getName(), true)) { + PsiClass superInterface = superMethod.getContainingClass(); + if (superInterface == null) { + continue; + } + + // calculate substitutor of containingClass --> inheritor + PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(containingClass, inheritor, PsiSubstitutor.EMPTY); + // calculate substitutor of inheritor --> superInterface + substitutor = TypeConversionUtil.getSuperClassSubstitutor(superInterface, inheritor, substitutor); + + final MethodSignature superSignature = superMethod.getSignature(substitutor); + final MethodSignature derivedSignature = method.getSignature(PsiSubstitutor.EMPTY); + boolean isOverridden = MethodSignatureUtil.isSubsignature(superSignature, derivedSignature); + + if (isOverridden) { + result[0] = superMethod; + return false; + } + } + } + return true; + } + }); + return result[0]; + } + + @NotNull + public static Map createSubClassCache() { + return new FactoryMap() { + @Nullable + @Override + protected PsiClass create(PsiClass aClass) { + return ClassInheritorsSearch.search(aClass, false).findFirst(); + } + }; + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.java index 4c705206a0a1..a0a7a0352ba5 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.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. @@ -31,8 +31,8 @@ import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; +import com.intellij.psi.impl.FindSuperElementsHelper; import com.intellij.psi.search.searches.AllOverridingMethodsSearch; -import com.intellij.psi.search.searches.ClassInheritorsSearch; import com.intellij.psi.search.searches.FunctionalExpressionSearch; import com.intellij.psi.search.searches.SuperMethodsSearch; import com.intellij.psi.util.MethodSignatureBackedByPsiMethod; @@ -48,12 +48,12 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.Set; public class JavaLineMarkerProvider implements LineMarkerProvider { - - protected final DaemonCodeAnalyzerSettings myDaemonSettings; - protected final EditorColorsManager myColorsManager; + private final DaemonCodeAnalyzerSettings myDaemonSettings; + private final EditorColorsManager myColorsManager; public JavaLineMarkerProvider(DaemonCodeAnalyzerSettings daemonSettings, EditorColorsManager colorsManager) { myDaemonSettings = daemonSettings; @@ -72,19 +72,14 @@ public class JavaLineMarkerProvider implements LineMarkerProvider { method.hasModifierProperty(PsiModifier.ABSTRACT) == superSignature.getMethod().hasModifierProperty(PsiModifier.ABSTRACT); final Icon icon = overrides ? AllIcons.Gutter.OverridingMethod : AllIcons.Gutter.ImplementingMethod; - final MarkerType type = MarkerType.OVERRIDING_METHOD; - ArrowUpLineMarkerInfo info = new ArrowUpLineMarkerInfo(element, icon, type); - return NavigateAction.setNavigateAction(info, "Go to super method", "GotoSuperMethod"); + return createSuperMethodLineMarkerInfo(element, icon, Pass.UPDATE_ALL); } } final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(element); final PsiElement firstChild = element.getFirstChild(); if (interfaceMethod != null && firstChild != null) { - final Icon icon = AllIcons.Gutter.ImplementingMethod; - final MarkerType type = MarkerType.OVERRIDING_METHOD; - ArrowUpLineMarkerInfo info = new ArrowUpLineMarkerInfo(firstChild, icon, type); - return NavigateAction.setNavigateAction(info, "Go to super method", "GotoSuperMethod"); + return createSuperMethodLineMarkerInfo(firstChild, AllIcons.Gutter.ImplementingMethod, Pass.UPDATE_ALL); } if (myDaemonSettings.SHOW_METHOD_SEPARATORS && firstChild == null) { @@ -128,6 +123,12 @@ public class JavaLineMarkerProvider implements LineMarkerProvider { return null; } + @NotNull + private static LineMarkerInfo createSuperMethodLineMarkerInfo(@NotNull PsiElement name, @NotNull Icon icon, int passId) { + ArrowUpLineMarkerInfo info = new ArrowUpLineMarkerInfo(name, icon, MarkerType.OVERRIDING_METHOD, passId); + return NavigateAction.setNavigateAction(info, "Go to super method", IdeActions.ACTION_GOTO_SUPER); + } + private static int getCategory(@NotNull PsiElement element, @NotNull CharSequence documentChars) { if (element instanceof PsiField || element instanceof PsiTypeParameter) return 1; if (element instanceof PsiClass || element instanceof PsiClassInitializer) return 2; @@ -147,37 +148,92 @@ public class JavaLineMarkerProvider implements LineMarkerProvider { @Override public void collectSlowLineMarkers(@NotNull final List elements, @NotNull final Collection result) { ApplicationManager.getApplication().assertReadAccessAllowed(); + Map subClassCache = FindSuperElementsHelper.createSubClassCache(); - Set methods = new HashSet(); + Collection methods = new THashSet(); //noinspection ForLoopReplaceableByForEach for (int i = 0; i < elements.size(); i++) { PsiElement element = elements.get(i); ProgressManager.checkCanceled(); - if (element instanceof PsiMethod) { - final PsiMethod method = (PsiMethod)element; + if (!(element instanceof PsiIdentifier)) continue; + PsiElement parent = element.getParent(); + if (parent instanceof PsiMethod) { + final PsiMethod method = (PsiMethod)parent; if (PsiUtil.canBeOverriden(method)) { methods.add(method); } } - else if (element instanceof PsiClass && !(element instanceof PsiTypeParameter)) { - collectInheritingClasses((PsiClass)element, result); + else if (parent instanceof PsiClass && !(parent instanceof PsiTypeParameter)) { + collectInheritingClasses((PsiClass)parent, result, subClassCache); } } if (!methods.isEmpty()) { - collectOverridingAccessors(methods, result); + collectOverridingMethods(methods, result); + collectSiblingInheritedMethods(methods, result, subClassCache); } } - public static void collectInheritingClasses(PsiClass aClass, Collection result) { + private static void collectSiblingInheritedMethods(@NotNull final Collection methods, + @NotNull Collection result, + @NotNull Map subClassCache) { + for (PsiMethod method : methods) { + ProgressManager.checkCanceled(); + PsiClass aClass = method.getContainingClass(); + if (aClass == null || aClass.hasModifierProperty(PsiModifier.FINAL) || aClass.isInterface()) continue; + + System.err.println("collectSiblingInheritedMethods for "+method+" in "+aClass.getQualifiedName()); + + boolean canHaveSiblingSuper = !method.hasModifierProperty(PsiModifier.ABSTRACT) && !method.hasModifierProperty(PsiModifier.STATIC) && method.hasModifierProperty(PsiModifier.PUBLIC)&& !method.hasModifierProperty(PsiModifier.FINAL)&& !method.hasModifierProperty(PsiModifier.NATIVE); + System.err.println("canHaveSiblingSuper = " + canHaveSiblingSuper); + if (!canHaveSiblingSuper) continue; + + PsiMethod siblingInheritedViaSubClass = FindSuperElementsHelper.getSiblingInheritedViaSubClass(method, subClassCache); + System.err.println("siblingInheritedViaSubClass = " + siblingInheritedViaSubClass); + if (siblingInheritedViaSubClass == null) { + continue; + } + PsiElement range = getMethodRange(method); + LineMarkerInfo info = createSuperMethodLineMarkerInfo(range, AllIcons.Gutter.ImplementingMethod, Pass.UPDATE_OVERRIDEN_MARKERS); + result.add(info); + PsiClass sClass = siblingInheritedViaSubClass.getContainingClass(); + String sName = sClass == null ? null : sClass.getQualifiedName(); + System.err.println("Added sibling "+siblingInheritedViaSubClass+" in "+sName+" to results: "+result); + } + } + + @NotNull + private static PsiElement getMethodRange(@NotNull PsiMethod method) { + PsiElement range; + if (method.isPhysical()) { + range = method.getNameIdentifier(); + } + else { + final PsiElement navigationElement = method.getNavigationElement(); + range = navigationElement instanceof PsiNameIdentifierOwner + ? ((PsiNameIdentifierOwner)navigationElement).getNameIdentifier() + : navigationElement; + } + if (range == null) { + range = method; + } + return range; + } + + public static void collectInheritingClasses(@NotNull PsiClass aClass, + @NotNull Collection result, + @NotNull Map subClassCache) { if (aClass.hasModifierProperty(PsiModifier.FINAL)) { return; } if (CommonClassNames.JAVA_LANG_OBJECT.equals(aClass.getQualifiedName())) return; // It's useless to have overridden markers for object. - if (ClassInheritorsSearch.search(aClass, false).findFirst() != null || FunctionalExpressionSearch.search(aClass).findFirst() != null) { + PsiClass subClass = subClassCache.get(aClass); + if (subClass != null || FunctionalExpressionSearch.search(aClass).findFirst() != null) { final Icon icon = aClass.isInterface() ? AllIcons.Gutter.ImplementedMethod : AllIcons.Gutter.OverridenMethod; PsiElement range = aClass.getNameIdentifier(); - if (range == null) range = aClass; + if (range == null) { + range = aClass; + } MarkerType type = MarkerType.SUBCLASSED_CLASS; LineMarkerInfo info = new LineMarkerInfo(range, range.getTextRange(), icon, Pass.UPDATE_OVERRIDEN_MARKERS, type.getTooltip(), @@ -188,7 +244,7 @@ public class JavaLineMarkerProvider implements LineMarkerProvider { } } - private static void collectOverridingAccessors(final Set methods, Collection result) { + private static void collectOverridingMethods(@NotNull final Collection methods, @NotNull Collection result) { final Set overridden = new HashSet(); Set classes = new THashSet(); for (PsiMethod method : methods) { @@ -229,22 +285,9 @@ public class JavaLineMarkerProvider implements LineMarkerProvider { ProgressManager.checkCanceled(); boolean overrides = !method.hasModifierProperty(PsiModifier.ABSTRACT); - final Icon icon = overrides ? AllIcons.Gutter.OverridenMethod : AllIcons.Gutter.ImplementedMethod; - PsiElement range; - if (method.isPhysical()) { - range = method.getNameIdentifier(); - } - else { - final PsiElement navigationElement = method.getNavigationElement(); - if (navigationElement instanceof PsiNameIdentifierOwner) { - range = ((PsiNameIdentifierOwner)navigationElement).getNameIdentifier(); - } - else { - range = navigationElement; - } - } - if (range == null) range = method; + PsiElement range = getMethodRange(method); final MarkerType type = MarkerType.OVERRIDDEN_METHOD; + final Icon icon = overrides ? AllIcons.Gutter.OverridenMethod : AllIcons.Gutter.ImplementedMethod; LineMarkerInfo info = new LineMarkerInfo(range, range.getTextRange(), icon, Pass.UPDATE_OVERRIDEN_MARKERS, type.getTooltip(), type.getNavigationHandler(), @@ -255,8 +298,8 @@ public class JavaLineMarkerProvider implements LineMarkerProvider { } private static class ArrowUpLineMarkerInfo extends MergeableLineMarkerInfo { - private ArrowUpLineMarkerInfo(@NotNull PsiElement element, Icon icon, @NotNull MarkerType markerType) { - super(element, element.getTextRange(), icon, Pass.UPDATE_ALL, markerType.getTooltip(), + private ArrowUpLineMarkerInfo(@NotNull PsiElement element, @NotNull Icon icon, @NotNull MarkerType markerType, int passId) { + super(element, element.getTextRange(), icon, passId, markerType.getTooltip(), markerType.getNavigationHandler(), GutterIconRenderer.Alignment.LEFT); } @@ -274,6 +317,7 @@ public class JavaLineMarkerProvider implements LineMarkerProvider { return myIcon; } + @NotNull @Override public Function getCommonTooltip(@NotNull List infos) { return new Function() { diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/MarkerType.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/MarkerType.java index 89785c572d84..d956ddcc054d 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/MarkerType.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/MarkerType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +34,7 @@ import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.util.Computable; import com.intellij.psi.*; +import com.intellij.psi.impl.FindSuperElementsHelper; import com.intellij.psi.search.PsiElementProcessor; import com.intellij.psi.search.PsiElementProcessorAdapter; import com.intellij.psi.search.SearchScope; @@ -45,6 +46,7 @@ import com.intellij.util.ArrayUtil; import com.intellij.util.CommonProcessors; import com.intellij.util.Function; import com.intellij.util.NullableFunction; +import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashSet; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -105,9 +107,9 @@ public class MarkerType { }); @Nullable - public static String calculateOverridingMethodTooltip(PsiMethod method, boolean acceptSelf) { + private static String calculateOverridingMethodTooltip(@NotNull PsiMethod method, boolean acceptSelf) { PsiMethod[] superMethods = composeSuperMethods(method, acceptSelf); - if (superMethods == null) return null; + if (superMethods.length == 0) return null; PsiMethod superMethod = superMethods[0]; boolean isAbstract = method.hasModifierProperty(PsiModifier.ABSTRACT); @@ -121,10 +123,11 @@ public class MarkerType { else{ key = sameSignature ? "method.overrides" : "method.overrides.in"; } - return composeText(superMethods, "", DaemonBundle.message(key), "GotoSuperMethod"); + return composeText(superMethods, "", DaemonBundle.message(key), IdeActions.ACTION_GOTO_SUPER); } - private static String composeText(PsiElement[] methods, String start, String pattern, String actionId) { + @NotNull + private static String composeText(@NotNull PsiElement[] methods, @NotNull String start, @NotNull String pattern, @NotNull String actionId) { Shortcut[] shortcuts = ActionManager.getInstance().getAction(actionId).getShortcutSet().getShortcuts(); Shortcut shortcut = ArrayUtil.getFirstElement(shortcuts); String postfix = "

Click"; @@ -133,9 +136,9 @@ public class MarkerType { return GutterIconTooltipHelper.composeText(Arrays.asList(methods), start, pattern, postfix); } - public static void navigateToOverridingMethod(MouseEvent e, PsiMethod method, boolean acceptSelf) { + private static void navigateToOverridingMethod(MouseEvent e, @NotNull PsiMethod method, boolean acceptSelf) { PsiMethod[] superMethods = composeSuperMethods(method, acceptSelf); - if (superMethods == null) return; + if (superMethods.length == 0) return; boolean showMethodNames = !PsiUtil.allMethodsHaveSameSignature(superMethods); PsiElementListNavigator.openTargets(e, superMethods, DaemonBundle.message("navigation.title.super.method", method.getName()), @@ -143,17 +146,23 @@ public class MarkerType { new MethodCellRenderer(showMethodNames)); } - @Nullable - private static PsiMethod[] composeSuperMethods(PsiMethod method, boolean acceptSelf) { - PsiMethod[] superMethods = method.findSuperMethods(false); + @NotNull + private static PsiMethod[] composeSuperMethods(@NotNull PsiMethod method, boolean acceptSelf) { + PsiElement[] superElements = FindSuperElementsHelper.findSuperElements(method); + + PsiMethod[] superMethods = ContainerUtil.map(superElements, new Function() { + @Override + public PsiMethod fun(PsiElement element) { + return (PsiMethod)element; + } + }, PsiMethod.EMPTY_ARRAY); if (acceptSelf) { superMethods = ArrayUtil.prepend(method, superMethods); } - if (superMethods.length == 0) return null; return superMethods; } - private static PsiElement getParentMethod(PsiElement element) { + private static PsiElement getParentMethod(@NotNull PsiElement element) { final PsiElement parent = element.getParent(); final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(parent); return interfaceMethod != null ? interfaceMethod : parent; @@ -179,7 +188,7 @@ public class MarkerType { } }); - public static String getOverriddenMethodTooltip(final PsiMethod method) { + private static String getOverriddenMethodTooltip(@NotNull PsiMethod method) { PsiElementProcessor.CollectElementsWithLimit processor = new PsiElementProcessor.CollectElementsWithLimit(5); OverridingMethodsSearch.search(method, true).forEach(new PsiElementProcessorAdapter(processor)); @@ -206,7 +215,7 @@ public class MarkerType { return composeText(overridings, start, pattern, IdeActions.ACTION_GOTO_IMPLEMENTATION); } - public static void navigateToOverriddenMethod(MouseEvent e, final PsiMethod method) { + private static void navigateToOverriddenMethod(MouseEvent e, @NotNull final PsiMethod method) { if (DumbService.isDumb(method.getProject())) { DumbService.getInstance(method.getProject()).showDumbModeNotification( "Navigation to overriding classes is not possible during index update"); @@ -267,7 +276,7 @@ public class MarkerType { } }); - public static String getSubclassedClassTooltip(PsiClass aClass) { + private static String getSubclassedClassTooltip(@NotNull PsiClass aClass) { PsiElementProcessor.CollectElementsWithLimit processor = new PsiElementProcessor.CollectElementsWithLimit(5, new THashSet()); ClassInheritorsSearch.search(aClass, true).forEach(new PsiElementProcessorAdapter(processor)); @@ -298,7 +307,7 @@ public class MarkerType { return composeText(subclasses, start, pattern, IdeActions.ACTION_GOTO_IMPLEMENTATION); } - public static void navigateToSubclassedClass(MouseEvent e, final PsiClass aClass) { + private static void navigateToSubclassedClass(MouseEvent e, @NotNull final PsiClass aClass) { if (DumbService.isDumb(aClass.getProject())) { DumbService.getInstance(aClass.getProject()).showDumbModeNotification("Navigation to overriding methods is not possible during index update"); return; @@ -331,7 +340,7 @@ public class MarkerType { private final PsiClass myClass; private final PsiClassOrFunctionalExpressionListCellRenderer myRenderer; - public SubclassUpdater(PsiClass aClass, PsiClassOrFunctionalExpressionListCellRenderer renderer) { + private SubclassUpdater(@NotNull PsiClass aClass, @NotNull PsiClassOrFunctionalExpressionListCellRenderer renderer) { super(aClass.getProject(), SEARCHING_FOR_OVERRIDDEN_METHODS); myClass = aClass; myRenderer = renderer; @@ -374,14 +383,13 @@ public class MarkerType { } }); } - } private static class OverridingMethodsUpdater extends ListBackgroundUpdaterTask { private final PsiMethod myMethod; private final PsiElementListCellRenderer myRenderer; - public OverridingMethodsUpdater(PsiMethod method, PsiElementListCellRenderer renderer) { + private OverridingMethodsUpdater(@NotNull PsiMethod method, @NotNull PsiElementListCellRenderer renderer) { super(method.getProject(), SEARCHING_FOR_OVERRIDING_METHODS); myMethod = method; myRenderer = renderer; diff --git a/java/java-impl/src/com/intellij/codeInsight/hint/actions/ShowSiblingsAction.java b/java/java-impl/src/com/intellij/codeInsight/hint/actions/ShowSiblingsAction.java index 41aea6e9c181..11a101da3edd 100644 --- a/java/java-impl/src/com/intellij/codeInsight/hint/actions/ShowSiblingsAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/hint/actions/ShowSiblingsAction.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. @@ -29,15 +29,11 @@ import com.intellij.psi.impl.FindSuperElementsHelper; import com.intellij.psi.presentation.java.SymbolPresentationUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.Consumer; -import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.NotNull; public class ShowSiblingsAction extends ShowImplementationsAction { - public ShowSiblingsAction() { - super(); - } - @Override - public void performForContext(DataContext dataContext, final boolean invokedByShortcut) { + public void performForContext(@NotNull DataContext dataContext, final boolean invokedByShortcut) { final Project project = CommonDataKeys.PROJECT.getData(dataContext); final PsiFile file = CommonDataKeys.PSI_FILE.getData(dataContext); @@ -61,7 +57,7 @@ public class ShowSiblingsAction extends ShowImplementationsAction { } final NavigatablePsiElement[] superElements = (NavigatablePsiElement[])findSuperElements(element); - if (superElements == null || superElements.length == 0) return; + if (superElements.length == 0) return; final boolean isMethod = superElements[0] instanceof PsiMethod; final JBPopup popup = PsiElementListNavigator.navigateOrCreatePopup(superElements, "Choose super " + (isMethod ? "method" : "class or interface"), "Super " + (isMethod ? "methods" : "classes/interfaces"), @@ -81,11 +77,11 @@ public class ShowSiblingsAction extends ShowImplementationsAction { } private void showSiblings(boolean invokedByShortcut, - Project project, + @NotNull Project project, Editor editor, PsiFile file, boolean invokedFromEditor, - PsiElement element) { + @NotNull PsiElement element) { final PsiElement[] impls = getSelfAndImplementations(editor, element, createImplementationsSearcher(), false); final String text = SymbolPresentationUtil.getSymbolPresentableText(element); showImplementations(impls, project, text, editor, file, element, invokedFromEditor, invokedByShortcut); @@ -96,11 +92,11 @@ public class ShowSiblingsAction extends ShowImplementationsAction { return false; } - @Nullable + @NotNull private static PsiElement[] findSuperElements(final PsiElement element) { PsiNameIdentifierOwner parent = PsiTreeUtil.getParentOfType(element, PsiMethod.class, PsiClass.class); if (parent == null) { - return null; + return PsiElement.EMPTY_ARRAY; } return FindSuperElementsHelper.findSuperElements(parent); diff --git a/java/java-impl/src/com/intellij/codeInsight/navigation/JavaGotoSuperHandler.java b/java/java-impl/src/com/intellij/codeInsight/navigation/JavaGotoSuperHandler.java index 95668b59fa69..1663d31da4a8 100644 --- a/java/java-impl/src/com/intellij/codeInsight/navigation/JavaGotoSuperHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/navigation/JavaGotoSuperHandler.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. @@ -32,7 +32,6 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; public class JavaGotoSuperHandler implements CodeInsightActionHandler { @Override @@ -41,7 +40,7 @@ public class JavaGotoSuperHandler implements CodeInsightActionHandler { int offset = editor.getCaretModel().getOffset(); PsiElement[] superElements = findSuperElements(file, offset); - if (superElements == null || superElements.length == 0) return; + if (superElements.length == 0) return; if (superElements.length == 1) { PsiElement superElement = superElements[0].getNavigationElement(); final PsiFile containingFile = superElement.getContainingFile(); @@ -50,24 +49,25 @@ public class JavaGotoSuperHandler implements CodeInsightActionHandler { if (virtualFile == null) return; OpenFileDescriptor descriptor = new OpenFileDescriptor(project, virtualFile, superElement.getTextOffset()); FileEditorManager.getInstance(project).openTextEditor(descriptor, true); - } else { - if (superElements[0] instanceof PsiMethod) { - boolean showMethodNames = !PsiUtil.allMethodsHaveSameSignature((PsiMethod[])superElements); - PsiElementListNavigator.openTargets(editor, (PsiMethod[])superElements, - CodeInsightBundle.message("goto.super.method.chooser.title"), - CodeInsightBundle.message("goto.super.method.findUsages.title", ((PsiMethod)superElements[0]).getName()), - new MethodCellRenderer(showMethodNames)); - } - else { - NavigationUtil.getPsiElementPopup(superElements, CodeInsightBundle.message("goto.super.class.chooser.title")).showInBestPositionFor(editor); - } + } + else if (superElements[0] instanceof PsiMethod) { + boolean showMethodNames = !PsiUtil.allMethodsHaveSameSignature((PsiMethod[])superElements); + PsiElementListNavigator.openTargets(editor, (PsiMethod[])superElements, + CodeInsightBundle.message("goto.super.method.chooser.title"), + CodeInsightBundle + .message("goto.super.method.findUsages.title", ((PsiMethod)superElements[0]).getName()), + new MethodCellRenderer(showMethodNames)); + } + else { + NavigationUtil.getPsiElementPopup(superElements, CodeInsightBundle.message("goto.super.class.chooser.title")) + .showInBestPositionFor(editor); } } - @Nullable - private PsiElement[] findSuperElements(PsiFile file, int offset) { + @NotNull + private PsiElement[] findSuperElements(@NotNull PsiFile file, int offset) { PsiElement element = getElement(file, offset); - if (element == null) return null; + if (element == null) return PsiElement.EMPTY_ARRAY; final PsiElement psiElement = PsiTreeUtil.getParentOfType(element, PsiFunctionalExpression.class, PsiMember.class); if (psiElement instanceof PsiFunctionalExpression) { @@ -79,13 +79,13 @@ public class JavaGotoSuperHandler implements CodeInsightActionHandler { final PsiNameIdentifierOwner parent = PsiTreeUtil.getNonStrictParentOfType(element, PsiMethod.class, PsiClass.class); if (parent == null) { - return null; + return PsiElement.EMPTY_ARRAY; } return FindSuperElementsHelper.findSuperElements(parent); } - protected PsiElement getElement(PsiFile file, int offset) { + protected PsiElement getElement(@NotNull PsiFile file, int offset) { return file.findElementAt(offset); } diff --git a/java/java-impl/src/com/intellij/ide/util/SuperMethodWarningUtil.java b/java/java-impl/src/com/intellij/ide/util/SuperMethodWarningUtil.java index 5e3a2d10c36d..a1a22653a60e 100644 --- a/java/java-impl/src/com/intellij/ide/util/SuperMethodWarningUtil.java +++ b/java/java-impl/src/com/intellij/ide/util/SuperMethodWarningUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiModifier; +import com.intellij.psi.impl.FindSuperElementsHelper; import com.intellij.psi.presentation.java.SymbolPresentationUtil; import com.intellij.psi.search.PsiElementProcessor; import com.intellij.psi.search.searches.DeepestSuperMethodsSearch; @@ -34,6 +35,7 @@ import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -41,20 +43,24 @@ public class SuperMethodWarningUtil { private SuperMethodWarningUtil() {} @NotNull - public static PsiMethod[] checkSuperMethods(final PsiMethod method, String actionString) { - return checkSuperMethods(method, actionString, null); + public static PsiMethod[] checkSuperMethods(@NotNull PsiMethod method, @NotNull String actionString) { + return checkSuperMethods(method, actionString, Collections.emptyList()); } @NotNull - public static PsiMethod[] checkSuperMethods(final PsiMethod method, String actionString, Collection ignore) { + public static PsiMethod[] checkSuperMethods(@NotNull PsiMethod method, @NotNull String actionString, @NotNull Collection ignore) { PsiClass aClass = method.getContainingClass(); if (aClass == null) return new PsiMethod[]{method}; final Collection superMethods = DeepestSuperMethodsSearch.search(method).findAll(); - if (ignore != null) { - superMethods.removeAll(ignore); - } + superMethods.removeAll(ignore); + if (superMethods.isEmpty()) { + PsiMethod siblingSuperMethod = FindSuperElementsHelper.getSiblingInheritedViaSubClass(method); + if (siblingSuperMethod != null) { + superMethods.add(siblingSuperMethod); + } + } if (superMethods.isEmpty()) return new PsiMethod[]{method}; @@ -85,7 +91,7 @@ public class SuperMethodWarningUtil { } - public static PsiMethod checkSuperMethod(final PsiMethod method, String actionString) { + public static PsiMethod checkSuperMethod(@NotNull PsiMethod method, @NotNull String actionString) { PsiClass aClass = method.getContainingClass(); if (aClass == null) return method; @@ -110,10 +116,10 @@ public class SuperMethodWarningUtil { return null; } - public static void checkSuperMethod(final PsiMethod method, - final String actionString, - final PsiElementProcessor processor, - final Editor editor) { + public static void checkSuperMethod(@NotNull PsiMethod method, + @NotNull String actionString, + @NotNull final PsiElementProcessor processor, + @NotNull Editor editor) { PsiClass aClass = method.getContainingClass(); if (aClass == null) { processor.execute(method); @@ -137,7 +143,7 @@ public class SuperMethodWarningUtil { return; } - final PsiMethod[] methods = new PsiMethod[]{superMethod, method}; + final PsiMethod[] methods = {superMethod, method}; final String renameBase = actionString + " base method"; final String renameCurrent = actionString + " only current method"; final JBList list = new JBList(renameBase, renameCurrent); @@ -148,6 +154,7 @@ public class SuperMethodWarningUtil { .setResizable(false) .setRequestFocus(true) .setItemChoosenCallback(new Runnable() { + @Override public void run() { final Object value = list.getSelectedValue(); if (value instanceof String) { diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaAllOverridingMethodsSearcher.java b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaAllOverridingMethodsSearcher.java index a614854ba728..797a22e8d68a 100644 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaAllOverridingMethodsSearcher.java +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaAllOverridingMethodsSearcher.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. @@ -61,7 +61,7 @@ public class JavaAllOverridingMethodsSearcher implements QueryExecutor allSupers = new ArrayList(Arrays.asList(aClass.getSupers())); - for (Iterator iterator = allSupers.iterator(); iterator.hasNext();) { - PsiClass superClass = iterator.next(); - if (CommonClassNames.JAVA_LANG_OBJECT.equals(superClass.getQualifiedName())) iterator.remove(); - } - return allSupers.toArray(new PsiClass[allSupers.size()]); - } else if (element instanceof PsiMethod) { - PsiMethod method = (PsiMethod) element; - if (method.isConstructor()) { - PsiMethod constructorInSuper = PsiSuperMethodUtil.findConstructorInSuper(method); - if (constructorInSuper != null) { - return new PsiMethod[]{constructorInSuper}; - } - } else { - return method.findSuperMethods(false); - } - } - return null; - } - -} diff --git a/java/java-tests/testData/codeInsight/gotosuper/SiblingInheritance.after.java b/java/java-tests/testData/codeInsight/gotosuper/SiblingInheritance.after.java new file mode 100644 index 000000000000..8c4289162396 --- /dev/null +++ b/java/java-tests/testData/codeInsight/gotosuper/SiblingInheritance.after.java @@ -0,0 +1,11 @@ +package z; + +interface I { + void run(); +} +abstract class A { + public void run() {} +} + +class Foo extends A implements I { +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/gotosuper/SiblingInheritance.java b/java/java-tests/testData/codeInsight/gotosuper/SiblingInheritance.java new file mode 100644 index 000000000000..d8d3e2970a93 --- /dev/null +++ b/java/java-tests/testData/codeInsight/gotosuper/SiblingInheritance.java @@ -0,0 +1,11 @@ +package z; + +interface I { + void run(); +} +abstract class A { + public void run() {} +} + +class Foo extends A implements I { +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/navigation/JavaGotoSuperTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/JavaGotoSuperTest.java similarity index 51% rename from java/java-tests/testSrc/com/intellij/codeInsight/navigation/JavaGotoSuperTest.java rename to java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/JavaGotoSuperTest.java index e666583564b0..1a04900bc371 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/navigation/JavaGotoSuperTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/JavaGotoSuperTest.java @@ -13,20 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.codeInsight.navigation; +package com.intellij.codeInsight.daemon.impl; import com.intellij.JavaTestUtil; import com.intellij.codeInsight.CodeInsightActionHandler; import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase; import com.intellij.codeInsight.daemon.LineMarkerInfo; -import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl; +import com.intellij.ide.DataManager; import com.intellij.lang.CodeInsightActions; import com.intellij.lang.java.JavaLanguage; -import com.intellij.openapi.actionSystem.ActionManager; -import com.intellij.openapi.actionSystem.IdeActions; -import com.intellij.openapi.actionSystem.Shortcut; +import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.editor.Document; import com.intellij.openapi.keymap.KeymapUtil; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -38,7 +39,7 @@ public class JavaGotoSuperTest extends LightDaemonAnalyzerTestCase { return JavaTestUtil.getJavaTestDataPath(); } - protected String getBasePath() { + private static String getBasePath() { return "/codeInsight/gotosuper/"; } @@ -46,6 +47,13 @@ public class JavaGotoSuperTest extends LightDaemonAnalyzerTestCase { doTest(); } + private void doTest() { + configureByFile(getBasePath() + getTestName(false) + ".java"); + final CodeInsightActionHandler handler = CodeInsightActions.GOTO_SUPER.forLanguage(JavaLanguage.INSTANCE); + handler.invoke(getProject(), getEditor(), getFile()); + checkResultByFile(getBasePath() + getTestName(false) + ".after.java"); + } + public void testLambdaMarker() throws Exception { configureByFile(getBasePath() + getTestName(false) + ".java"); int offset = myEditor.getCaretModel().getOffset(); @@ -67,10 +75,52 @@ public class JavaGotoSuperTest extends LightDaemonAnalyzerTestCase { fail("Gutter expected"); } - private void doTest() throws Throwable { - configureByFile(getBasePath() + getTestName(false) + ".java"); - final CodeInsightActionHandler handler = CodeInsightActions.GOTO_SUPER.forLanguage(JavaLanguage.INSTANCE); - handler.invoke(getProject(), getEditor(), getFile()); - checkResultByFile(getBasePath() + getTestName(false) + ".after.java"); + public void testSiblingInheritance() throws Throwable { + doTest(); } + + public void testSiblingInheritanceLineMarkers() throws Throwable { + configureByFile(getBasePath() + "SiblingInheritance.java"); + PsiJavaFile file = (PsiJavaFile)getFile(); + PsiClass i = JavaPsiFacade.getInstance(getProject()).findClass("z.I", GlobalSearchScope.fileScope(file)); + PsiClass a = JavaPsiFacade.getInstance(getProject()).findClass("z.A", GlobalSearchScope.fileScope(file)); + PsiMethod iRun = i.getMethods()[0]; + assertEquals("run", iRun.getName()); + PsiMethod aRun = a.getMethods()[0]; + assertEquals("run", aRun.getName()); + doHighlighting(); + Document document = getEditor().getDocument(); + List markers = DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject()); + assertTrue(markers.size() >= 2); + LineMarkerInfo iMarker = findMarkerWithElement(markers, iRun.getNameIdentifier()); + assertSame(MarkerType.OVERRIDDEN_METHOD.getNavigationHandler(), iMarker.getNavigationHandler()); + + LineMarkerInfo aMarker = findMarkerWithElement(markers, aRun.getNameIdentifier()); + assertSame(MarkerType.OVERRIDING_METHOD.getNavigationHandler(), aMarker.getNavigationHandler()); + } + + private static LineMarkerInfo findMarkerWithElement(List markers, PsiElement psiMethod) { + LineMarkerInfo marker = ContainerUtil.find(markers, info -> { + return info.getElement().equals(psiMethod); + }); + assertNotNull(markers.toString(), marker); + return marker; + } + + public void testSiblingInheritanceGoDown() throws Throwable { + configureByFile(getBasePath() + "SiblingInheritance.after.java"); + AnAction action = ActionManager.getInstance().getAction(IdeActions.ACTION_GOTO_IMPLEMENTATION); + AnActionEvent event = new AnActionEvent( + null, + DataManager.getInstance().getDataContextFromFocus().getResultSync(), + "", + action.getTemplatePresentation(), + ActionManager.getInstance(), + 0); + action.update(event); + assertTrue(event.getPresentation().isEnabledAndVisible()); + action.actionPerformed(event); + checkResultByFile(getBasePath() + "SiblingInheritance.java"); + } + } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/PublicMethodNotExposedInInterfaceInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/PublicMethodNotExposedInInterfaceInspectionBase.java index ca8ec8f0ea0d..d5c2cacee541 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/PublicMethodNotExposedInInterfaceInspectionBase.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/PublicMethodNotExposedInInterfaceInspectionBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,8 @@ package com.siyeh.ig.abstraction; import com.intellij.codeInsight.AnnotationUtil; import com.intellij.psi.*; +import com.intellij.psi.impl.FindSuperElementsHelper; +import com.intellij.util.ArrayUtil; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; @@ -28,10 +30,10 @@ import com.siyeh.ig.ui.ExternalizableStringSet; import org.jetbrains.annotations.NotNull; public class PublicMethodNotExposedInInterfaceInspectionBase extends BaseInspection { - @SuppressWarnings({"PublicField"}) + @SuppressWarnings("PublicField") public final ExternalizableStringSet ignorableAnnotations = new ExternalizableStringSet(); - @SuppressWarnings({"PublicField"}) + @SuppressWarnings("PublicField") public boolean onlyWarnIfContainingClassImplementsAnInterface = false; @Override @@ -115,7 +117,11 @@ public class PublicMethodNotExposedInInterfaceInspectionBase extends BaseInspect } private boolean exposedInInterface(PsiMethod method) { - final PsiMethod[] superMethods = method.findSuperMethods(); + PsiMethod[] superMethods = method.findSuperMethods(); + PsiMethod siblingInherited = FindSuperElementsHelper.getSiblingInheritedViaSubClass(method); + if (siblingInherited != null && !ArrayUtil.contains(siblingInherited, superMethods)) { + superMethods = ArrayUtil.append(superMethods, siblingInherited); + } for (final PsiMethod superMethod : superMethods) { final PsiClass superClass = superMethod.getContainingClass(); if (superClass == null) { diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/NoopMethodInAbstractClassInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/NoopMethodInAbstractClassInspection.java index 375807f13b3e..74ac21ee25cc 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/NoopMethodInAbstractClassInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/NoopMethodInAbstractClassInspection.java @@ -18,6 +18,7 @@ package com.siyeh.ig.classlayout; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiModifier; +import com.intellij.psi.impl.FindSuperElementsHelper; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; @@ -67,6 +68,10 @@ public class NoopMethodInAbstractClassInspection extends BaseInspection { if (!MethodUtils.isEmpty(method)) { return; } + if (FindSuperElementsHelper.getSiblingInheritedViaSubClass(method) != null) { + // it may be an explicit intention to have non-abstract method here in order to sibling-inherit the method in subclass + return; + } registerMethodError(method); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInsight/GroovyLineMarkerProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInsight/GroovyLineMarkerProvider.java index 17045deb6ff7..7bfc95d97102 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInsight/GroovyLineMarkerProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInsight/GroovyLineMarkerProvider.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. @@ -33,6 +33,7 @@ import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; +import com.intellij.psi.impl.FindSuperElementsHelper; import com.intellij.psi.impl.PsiImplUtil; import com.intellij.psi.search.searches.AllOverridingMethodsSearch; import com.intellij.psi.search.searches.SuperMethodsSearch; @@ -60,18 +61,15 @@ import org.jetbrains.plugins.groovy.lang.psi.util.GrTraitUtil; import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils; import javax.swing.*; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Set; +import java.util.*; /** * @author ilyas * Same logic as for Java LMP */ public class GroovyLineMarkerProvider implements LineMarkerProvider { - protected final DaemonCodeAnalyzerSettings myDaemonSettings; - protected final EditorColorsManager myColorsManager; + private final DaemonCodeAnalyzerSettings myDaemonSettings; + private final EditorColorsManager myColorsManager; public GroovyLineMarkerProvider(DaemonCodeAnalyzerSettings daemonSettings, EditorColorsManager colorsManager) { myDaemonSettings = daemonSettings; @@ -152,7 +150,7 @@ public class GroovyLineMarkerProvider implements LineMarkerProvider { return null; } - private static boolean hasSuperMethods(GrMethod method) { + private static boolean hasSuperMethods(@NotNull GrMethod method) { final GrReflectedMethod[] reflectedMethods = method.getReflectedMethods(); if (reflectedMethods.length > 0) { for (GrReflectedMethod reflectedMethod : reflectedMethods) { @@ -166,7 +164,7 @@ public class GroovyLineMarkerProvider implements LineMarkerProvider { } } - private static int getGroovyCategory(PsiElement element, CharSequence documentChars) { + private static int getGroovyCategory(@NotNull PsiElement element, @NotNull CharSequence documentChars) { if (element instanceof GrVariableDeclarationImpl) { GrVariable[] variables = ((GrVariableDeclarationImpl)element).getVariables(); if (variables.length == 1 && variables[0] instanceof GrField && variables[0].getInitializerGroovy() instanceof GrClosableBlock) { @@ -193,6 +191,7 @@ public class GroovyLineMarkerProvider implements LineMarkerProvider { @Override public void collectSlowLineMarkers(@NotNull final List elements, @NotNull final Collection result) { Set methods = new HashSet(); + Map subClassCache = FindSuperElementsHelper.createSubClassCache(); for (PsiElement element : elements) { ProgressManager.checkCanceled(); if (element instanceof GrField) { @@ -208,13 +207,13 @@ public class GroovyLineMarkerProvider implements LineMarkerProvider { } } else if (element instanceof PsiClass && !(element instanceof PsiTypeParameter)) { - JavaLineMarkerProvider.collectInheritingClasses((PsiClass)element, result); + JavaLineMarkerProvider.collectInheritingClasses((PsiClass)element, result, subClassCache); } } collectOverridingMethods(methods, result); } - private static void collectOverridingMethods(final Set methods, Collection result) { + private static void collectOverridingMethods(@NotNull final Set methods, @NotNull Collection result) { final Set overridden = new HashSet(); Set classes = new THashSet(); @@ -264,7 +263,7 @@ public class GroovyLineMarkerProvider implements LineMarkerProvider { } } - private static boolean isCorrectTarget(PsiMethod method) { + private static boolean isCorrectTarget(@NotNull PsiMethod method) { if (method instanceof GrTraitMethod) return false; final PsiElement navigationElement = method.getNavigationElement(); From 41cb05a58e73dde5ec9b0170d05ab315b1960273 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 1 Jul 2015 20:28:11 +0300 Subject: [PATCH 054/137] notnull --- .../com/intellij/spi/SPIGotoSuperHandler.java | 5 +- .../daemon/impl/PsiElementListNavigator.java | 7 +- .../actions/ShowImplementationsAction.java | 69 +++++++++++-------- .../navigation/BackgroundUpdaterTask.java | 6 +- 4 files changed, 48 insertions(+), 39 deletions(-) diff --git a/java/java-impl/src/com/intellij/spi/SPIGotoSuperHandler.java b/java/java-impl/src/com/intellij/spi/SPIGotoSuperHandler.java index 966a7a3422ea..4be83981446e 100644 --- a/java/java-impl/src/com/intellij/spi/SPIGotoSuperHandler.java +++ b/java/java-impl/src/com/intellij/spi/SPIGotoSuperHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,13 +20,14 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.spi.psi.SPIClassProviderReferenceElement; +import org.jetbrains.annotations.NotNull; /** * User: anna */ public class SPIGotoSuperHandler extends JavaGotoSuperHandler { @Override - protected PsiElement getElement(PsiFile file, int offset) { + protected PsiElement getElement(@NotNull PsiFile file, int offset) { final SPIClassProviderReferenceElement providerElement = PsiTreeUtil.getParentOfType(super.getElement(file, offset), SPIClassProviderReferenceElement.class); if (providerElement != null) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PsiElementListNavigator.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PsiElementListNavigator.java index 57f65283fd2f..4eeca2b71704 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PsiElementListNavigator.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PsiElementListNavigator.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +37,7 @@ import com.intellij.ui.popup.HintUpdateSupply; import com.intellij.usages.UsageView; import com.intellij.util.Consumer; import com.intellij.util.Processor; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -88,12 +89,12 @@ public class PsiElementListNavigator { } @Nullable - public static JBPopup navigateOrCreatePopup(final NavigatablePsiElement[] targets, + public static JBPopup navigateOrCreatePopup(@NotNull final NavigatablePsiElement[] targets, final String title, final String findUsagesTitle, final ListCellRenderer listRenderer, @Nullable final ListBackgroundUpdaterTask listUpdaterTask, - final Consumer consumer) { + @NotNull final Consumer consumer) { if (targets.length == 0) return null; if (targets.length == 1) { consumer.consume(targets); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hint/actions/ShowImplementationsAction.java b/platform/lang-impl/src/com/intellij/codeInsight/hint/actions/ShowImplementationsAction.java index 7c3a0ced14d7..c48ae61e93c6 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hint/actions/ShowImplementationsAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/hint/actions/ShowImplementationsAction.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. @@ -105,7 +105,7 @@ public class ShowImplementationsAction extends AnAction implements PopupAction { } - protected Editor getEditor(DataContext dataContext) { + protected static Editor getEditor(@NotNull DataContext dataContext) { Editor editor = CommonDataKeys.EDITOR.getData(dataContext); if (editor == null) { @@ -123,7 +123,7 @@ public class ShowImplementationsAction extends AnAction implements PopupAction { return editor; } - public void performForContext(DataContext dataContext, boolean invokedByShortcut) { + public void performForContext(@NotNull DataContext dataContext, boolean invokedByShortcut) { final Project project = CommonDataKeys.PROJECT.getData(dataContext); if (project == null) return; PsiDocumentManager.getInstance(project).commitAllDocuments(); @@ -185,7 +185,7 @@ public class ShowImplementationsAction extends AnAction implements PopupAction { showImplementations(impls, project, text, editor, file, element, isInvokedFromEditor, invokedByShortcut); } - protected static PsiElement getElement(Project project, PsiFile file, Editor editor, PsiElement element) { + protected static PsiElement getElement(@NotNull Project project, PsiFile file, Editor editor, PsiElement element) { if (element == null && editor != null) { element = TargetElementUtil.findTargetElement(editor, TargetElementUtil.getInstance().getAllAccepted()); final PsiElement adjustedElement = @@ -200,6 +200,7 @@ public class ShowImplementationsAction extends AnAction implements PopupAction { return element; } + @NotNull protected static ImplementationSearcher createImplementationsSearcher() { if (ApplicationManager.getApplication().isUnitTestMode()) { return new ImplementationSearcher() { @@ -209,18 +210,16 @@ public class ShowImplementationsAction extends AnAction implements PopupAction { } }; } - else { - return new ImplementationSearcher.FirstImplementationsSearcher() { - @Override - protected PsiElement[] filterElements(PsiElement element, PsiElement[] targetElements, final int offset) { - return ShowImplementationsAction.filterElements(targetElements); - } - }; - } + return new ImplementationSearcher.FirstImplementationsSearcher() { + @Override + protected PsiElement[] filterElements(PsiElement element, PsiElement[] targetElements, final int offset) { + return ShowImplementationsAction.filterElements(targetElements); + } + }; } - protected void updateElementImplementations(final PsiElement element, final Editor editor, final Project project, final PsiFile file) { - PsiElement[] impls = null; + private void updateElementImplementations(final PsiElement element, final Editor editor, @NotNull Project project, final PsiFile file) { + PsiElement[] impls = {}; String text = ""; if (element != null) { // if (element instanceof PsiPackage) return; @@ -234,10 +233,15 @@ public class ShowImplementationsAction extends AnAction implements PopupAction { showImplementations(impls, project, text, editor, file, element, false, false); } - protected void showImplementations(final PsiElement[] impls, final Project project, final String text, final Editor editor, final PsiFile file, + protected void showImplementations(@NotNull PsiElement[] impls, + @NotNull final Project project, + final String text, + final Editor editor, + final PsiFile file, final PsiElement element, - boolean invokedFromEditor, boolean invokedByShortcut) { - if (impls == null || impls.length == 0) return; + boolean invokedFromEditor, + boolean invokedByShortcut) { + if (impls.length == 0) return; FeatureUsageTracker.getInstance().triggerFeatureUsed(CODEASSISTS_QUICKDEFINITION_FEATURE); if (LookupManager.getInstance(project).getActiveLookup() != null) { @@ -317,7 +321,7 @@ public class ShowImplementationsAction extends AnAction implements PopupAction { } } - private static boolean cancelTask(ImplementationsUpdaterTask task) { + private static boolean cancelTask(@Nullable ImplementationsUpdaterTask task) { if (task != null) { ProgressIndicator indicator = task.myIndicator; if (indicator != null) { @@ -330,9 +334,10 @@ public class ShowImplementationsAction extends AnAction implements PopupAction { private void updateInBackground(Editor editor, @Nullable PsiElement element, - ImplementationViewComponent component, + @NotNull ImplementationViewComponent component, String title, - AbstractPopup popup, Ref usageView) { + @NotNull AbstractPopup popup, + @NotNull Ref usageView) { final ImplementationsUpdaterTask updaterTask = SoftReference.dereference(myTaskRef); cancelTask(updaterTask); @@ -348,15 +353,17 @@ public class ShowImplementationsAction extends AnAction implements PopupAction { return true; } + @NotNull private static PsiElement[] getSelfAndImplementations(Editor editor, - PsiElement element, - final ImplementationSearcher handler) { + @NotNull PsiElement element, + @NotNull ImplementationSearcher handler) { return getSelfAndImplementations(editor, element, handler, !(element instanceof PomTargetPsiElement)); } + @NotNull protected static PsiElement[] getSelfAndImplementations(Editor editor, - PsiElement element, - final ImplementationSearcher handler, + @NotNull PsiElement element, + @NotNull ImplementationSearcher handler, final boolean includeSelfAlways) { int offset = editor == null ? 0 : editor.getCaretModel().getOffset(); final PsiElement[] handlerImplementations = handler.searchImplementations(element, editor, offset, includeSelfAlways, true); @@ -367,17 +374,18 @@ public class ShowImplementationsAction extends AnAction implements PopupAction { // Magically, it's null for ant property declarations. element = element.getNavigationElement(); psiFile = element.getContainingFile(); - if (psiFile == null) return PsiElement.EMPTY_ARRAY; + if (psiFile == null) { + return PsiElement.EMPTY_ARRAY; + } } if (psiFile.getVirtualFile() != null && (element.getTextRange() != null || element instanceof PsiFile)) { return new PsiElement[]{element}; } - else { - return PsiElement.EMPTY_ARRAY; - } + return PsiElement.EMPTY_ARRAY; } - private static PsiElement[] filterElements(final PsiElement[] targetElements) { + @NotNull + private static PsiElement[] filterElements(@NotNull final PsiElement[] targetElements) { final Set unique = new LinkedHashSet(Arrays.asList(targetElements)); for (final PsiElement elt : targetElements) { ApplicationManager.getApplication().runReadAction(new Runnable() { @@ -410,12 +418,13 @@ public class ShowImplementationsAction extends AnAction implements PopupAction { private static class ImplementationsUpdaterTask extends BackgroundUpdaterTask { private final String myCaption; private final Editor myEditor; + @NotNull private final PsiElement myElement; private final boolean myIncludeSelf; private PsiElement[] myElements; private volatile ProgressIndicator myIndicator; - public ImplementationsUpdaterTask(final PsiElement element, final Editor editor, final String caption, boolean includeSelf) { + private ImplementationsUpdaterTask(@NotNull PsiElement element, final Editor editor, final String caption, boolean includeSelf) { super(element.getProject(), ImplementationSearcher.SEARCHING_FOR_IMPLEMENTATIONS); myCaption = caption; myEditor = editor; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/BackgroundUpdaterTask.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/BackgroundUpdaterTask.java index 397889c8e1cf..85cd0952c0ad 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/navigation/BackgroundUpdaterTask.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/BackgroundUpdaterTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,8 +21,6 @@ import com.intellij.openapi.progress.PerformInBackgroundOption; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.popup.JBPopupAdapter; -import com.intellij.openapi.ui.popup.LightweightWindowEvent; import com.intellij.openapi.util.Ref; import com.intellij.psi.PsiElement; import com.intellij.ui.popup.AbstractPopup; @@ -69,7 +67,7 @@ public abstract class BackgroundUpdaterTask extends Task.Backgroundable { super(project, title, canBeCancelled, backgroundOption); } - public void init(@NotNull AbstractPopup popup, T component, Ref usageView) { + public void init(@NotNull AbstractPopup popup, @NotNull T component, @NotNull Ref usageView) { myPopup = popup; myComponent = component; myUsageView = usageView; From 551081321065599e323bab98a8882402ad21ea68 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 1 Jul 2015 20:34:19 +0300 Subject: [PATCH 055/137] optimisation --- .../src/com/intellij/psi/SingleRootFileViewProvider.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/platform/core-impl/src/com/intellij/psi/SingleRootFileViewProvider.java b/platform/core-impl/src/com/intellij/psi/SingleRootFileViewProvider.java index 9741110e23cf..28e62c06c0e4 100644 --- a/platform/core-impl/src/com/intellij/psi/SingleRootFileViewProvider.java +++ b/platform/core-impl/src/com/intellij/psi/SingleRootFileViewProvider.java @@ -179,13 +179,14 @@ public class SingleRootFileViewProvider extends UserDataHolderBase implements Fi } boolean set = myPsiFile.compareAndSet(null, psiFile); if (!set) { + PsiFile alreadyCreated = myPsiFile.get(); + if (alreadyCreated == psiFile) { + LOG.error(this + ".createFile() must create new file instance but got the same: " + psiFile); + } if (psiFile instanceof PsiFileImpl) { - if (myPsiFile.get() == psiFile) { - LOG.error(this + ".createFile() must create new file instance but got the same: " + psiFile); - } ((PsiFileImpl)psiFile).markInvalidated(); } - psiFile = myPsiFile.get(); + psiFile = alreadyCreated; } } return psiFile == PsiUtilCore.NULL_PSI_FILE ? null : psiFile; From e5795c2533f0fd6db475aaad02839a28f93eeade Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Thu, 2 Jul 2015 12:27:05 +0300 Subject: [PATCH 056/137] avoid invalidation of NULL_PSI_FILE --- .../src/com/intellij/psi/SingleRootFileViewProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/core-impl/src/com/intellij/psi/SingleRootFileViewProvider.java b/platform/core-impl/src/com/intellij/psi/SingleRootFileViewProvider.java index 28e62c06c0e4..c2ef19131a90 100644 --- a/platform/core-impl/src/com/intellij/psi/SingleRootFileViewProvider.java +++ b/platform/core-impl/src/com/intellij/psi/SingleRootFileViewProvider.java @@ -178,7 +178,7 @@ public class SingleRootFileViewProvider extends UserDataHolderBase implements Fi psiFile = PsiUtilCore.NULL_PSI_FILE; } boolean set = myPsiFile.compareAndSet(null, psiFile); - if (!set) { + if (!set && psiFile != PsiUtilCore.NULL_PSI_FILE) { PsiFile alreadyCreated = myPsiFile.get(); if (alreadyCreated == psiFile) { LOG.error(this + ".createFile() must create new file instance but got the same: " + psiFile); From bbb6fa6d0106faeeb572e50a1b277927bb2b944d Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Thu, 2 Jul 2015 12:31:51 +0300 Subject: [PATCH 057/137] removed diagnostic println --- .../codeInsight/daemon/impl/JavaLineMarkerProvider.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.java index a0a7a0352ba5..4faf4245b8ba 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.java @@ -181,14 +181,10 @@ public class JavaLineMarkerProvider implements LineMarkerProvider { PsiClass aClass = method.getContainingClass(); if (aClass == null || aClass.hasModifierProperty(PsiModifier.FINAL) || aClass.isInterface()) continue; - System.err.println("collectSiblingInheritedMethods for "+method+" in "+aClass.getQualifiedName()); - boolean canHaveSiblingSuper = !method.hasModifierProperty(PsiModifier.ABSTRACT) && !method.hasModifierProperty(PsiModifier.STATIC) && method.hasModifierProperty(PsiModifier.PUBLIC)&& !method.hasModifierProperty(PsiModifier.FINAL)&& !method.hasModifierProperty(PsiModifier.NATIVE); - System.err.println("canHaveSiblingSuper = " + canHaveSiblingSuper); if (!canHaveSiblingSuper) continue; PsiMethod siblingInheritedViaSubClass = FindSuperElementsHelper.getSiblingInheritedViaSubClass(method, subClassCache); - System.err.println("siblingInheritedViaSubClass = " + siblingInheritedViaSubClass); if (siblingInheritedViaSubClass == null) { continue; } @@ -197,7 +193,6 @@ public class JavaLineMarkerProvider implements LineMarkerProvider { result.add(info); PsiClass sClass = siblingInheritedViaSubClass.getContainingClass(); String sName = sClass == null ? null : sClass.getQualifiedName(); - System.err.println("Added sibling "+siblingInheritedViaSubClass+" in "+sName+" to results: "+result); } } From afac0df426b33189617dace7ebfe60f877d3b030 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Thu, 2 Jul 2015 12:33:07 +0300 Subject: [PATCH 058/137] speed optimisation: store computed hashCode --- .../util/text/ByteArrayCharSequence.java | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/platform/util/src/com/intellij/util/text/ByteArrayCharSequence.java b/platform/util/src/com/intellij/util/text/ByteArrayCharSequence.java index fcf7ea4f1dbf..c417c3628075 100644 --- a/platform/util/src/com/intellij/util/text/ByteArrayCharSequence.java +++ b/platform/util/src/com/intellij/util/text/ByteArrayCharSequence.java @@ -15,15 +15,36 @@ */ package com.intellij.util.text; +import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; public class ByteArrayCharSequence implements CharSequence { + private int hash; private final byte[] myChars; - public ByteArrayCharSequence(@NotNull byte... chars) { + private ByteArrayCharSequence(@NotNull byte[] chars) { myChars = chars; } + @Override + public int hashCode() { + int h = hash; + if (h == 0) { + byte[] chars = myChars; + + for (byte aChar : chars) { + h = 31 * h + aChar; + } + hash = h; + } + return h; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof CharSequence && StringUtil.equals(this, (CharSequence)obj); + } + @Override public final int length() { return myChars.length; @@ -63,6 +84,7 @@ public class ByteArrayCharSequence implements CharSequence { //noinspection RedundantStringConstructorCall return new String(name.toString()); // So we don't hold whole char[] buffer of a lengthy path on JDK 6 } + bytes[i] = (byte)c; } return new ByteArrayCharSequence(bytes); From 8144b12a672da6628928b2e6816d80f35e4f1509 Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 2 Jul 2015 11:53:35 +0200 Subject: [PATCH 059/137] prohibit cached values in vfs user data --- .../openapi/vfs/newvfs/impl/VirtualDirectoryImpl.java | 11 +++++++++++ .../openapi/vfs/newvfs/impl/VirtualFileImpl.java | 1 + 2 files changed, 12 insertions(+) diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualDirectoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualDirectoryImpl.java index 37b17c080549..a49df7b86027 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualDirectoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualDirectoryImpl.java @@ -18,6 +18,7 @@ package com.intellij.openapi.vfs.newvfs.impl; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.impl.ApplicationInfoImpl; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileAttributes; import com.intellij.openapi.util.io.FileUtilRt; @@ -32,6 +33,7 @@ import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent; import com.intellij.openapi.vfs.newvfs.persistent.FSRecords; import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS; import com.intellij.openapi.vfs.newvfs.persistent.PersistentFSImpl; +import com.intellij.psi.impl.PsiCachedValue; import com.intellij.util.ArrayUtil; import com.intellij.util.Function; import com.intellij.util.UriUtil; @@ -547,6 +549,15 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { @Override protected boolean changeUserMap(KeyFMap oldMap, KeyFMap newMap) { + checkLeaks(newMap); return myData.changeUserMap(oldMap, UserDataInterner.internUserData(newMap)); } + + static void checkLeaks(KeyFMap newMap) { + for (Key key : newMap.getKeys()) { + if (key != null && newMap.get(key) instanceof PsiCachedValue) { + throw new AssertionError("Don't store CachedValue in VFS user data, since it leads to memory leaks"); + } + } + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualFileImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualFileImpl.java index bea852bba7c5..c51a6e62d17d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualFileImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualFileImpl.java @@ -142,6 +142,7 @@ public class VirtualFileImpl extends VirtualFileSystemEntry { @Override protected boolean changeUserMap(KeyFMap oldMap, KeyFMap newMap) { + VirtualDirectoryImpl.checkLeaks(newMap); return mySegment.changeUserMap(Math.abs(getId()), oldMap, UserDataInterner.internUserData(newMap)); } From 6f7ff1646571048ae9e894b536949e19d190d7cc Mon Sep 17 00:00:00 2001 From: Manuel Stadelmann Date: Thu, 2 Jul 2015 12:55:49 +0300 Subject: [PATCH 060/137] RemoveUsageAction: Now selects a next item from the usage tree after removing one or several items. This makes the function easier to use with the keyboard. --- .../usages/actions/RemoveUsageAction.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/platform/usageView/src/com/intellij/usages/actions/RemoveUsageAction.java b/platform/usageView/src/com/intellij/usages/actions/RemoveUsageAction.java index 1d5b9e70cfb1..920575fa4cfc 100644 --- a/platform/usageView/src/com/intellij/usages/actions/RemoveUsageAction.java +++ b/platform/usageView/src/com/intellij/usages/actions/RemoveUsageAction.java @@ -18,14 +18,40 @@ package com.intellij.usages.actions; import com.intellij.usages.Usage; import com.intellij.usages.UsageView; +import java.util.List; + /** * @author Manuel Stadelmann */ public class RemoveUsageAction extends IncludeExcludeActionBase { + @Override protected void process(Usage[] usages, UsageView usageView) { + + Usage nextToSelect = null; + for (Usage usage : usages) { + Usage toSelect = getNextToSelect(usageView, usage); usageView.removeUsage(usage); + nextToSelect = toSelect; + } + + if (nextToSelect != null) { + usageView.selectUsages(new Usage[]{nextToSelect}); } } + + private Usage getNextToSelect(UsageView usageView, Usage toDelete) { + List sortedUsages = usageView.getSortedUsages(); + int curIndex = sortedUsages.indexOf(toDelete); + + int selectIndex = 0; + if (curIndex < sortedUsages.size() - 1) { + selectIndex = curIndex + 1; + } + else if (curIndex > 0) { + selectIndex = curIndex - 1; + } + return sortedUsages.get(selectIndex); + } } From d97756ee9fb4bfc9487aa3c69cf93bd8e4bc29f0 Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Thu, 2 Jul 2015 13:08:18 +0300 Subject: [PATCH 061/137] IDEA-90072 Faux italics used for some fonts with true italics available --- .../impl/ComplementaryFontsRegistry.java | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/ComplementaryFontsRegistry.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/ComplementaryFontsRegistry.java index 48e8de5f769b..e1eab3ac56a9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/ComplementaryFontsRegistry.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/ComplementaryFontsRegistry.java @@ -43,7 +43,13 @@ public class ComplementaryFontsRegistry { private static FontInfo ourSharedDefaultFont; private static final TIntHashSet ourUndisplayableChars = new TIntHashSet(); private static boolean ourOldUseAntialiasing; - + + // This matches style detection in JDK (class sun.font.Font2D) + private static final String[] BOLD_NAMES = {"bold", "demibold", "demi-bold", "demi bold", "negreta", "demi" }; + private static final String[] ITALIC_NAMES = {"italic", "cursiva", "oblique", "inclined"}; + private static final String[] BOLD_ITALIC_NAMES = {"bolditalic", "bold-italic", "bold italic", "boldoblique", "bold-oblique", + "bold oblique", "demibold italic", "negreta cursiva","demi oblique"}; + static { final UISettings settings = UISettings.getInstance(); ourOldUseAntialiasing = settings.ANTIALIASING_IN_EDITOR; @@ -121,35 +127,38 @@ public class ComplementaryFontsRegistry { Font[] allFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts(); for (Font font : allFonts) { String name = font.getName(); - int style; - if (name.endsWith("-Italic")) { - style = Font.ITALIC; - } - else if (name.endsWith("-Bold")) { - style = Font.BOLD; - } - else if (name.endsWith("-BoldItalic")) { - style = Font.BOLD | Font.ITALIC; - } - else { - style = Font.PLAIN; - } + int style = getFontStyle(name); if (style != Font.PLAIN) { - String baseName = name.substring(0, name.lastIndexOf('-')); - Pair[] entry = ourStyledFontMap.get(baseName); + String familyName = font.getFamily(); + Pair[] entry = ourStyledFontMap.get(familyName); if (entry == null) { //noinspection unchecked entry = new Pair[4]; for (int i = 1; i < 4; i++) { - entry[i] = Pair.create(baseName, i); + entry[i] = Pair.create(familyName, i); } - ourStyledFontMap.put(baseName, entry); + ourStyledFontMap.put(familyName, entry); } entry[style] = Pair.create(name, Font.PLAIN); } } } + @JdkConstants.FontStyle + private static int getFontStyle(String fontName) { + fontName = fontName.toLowerCase(Locale.getDefault()); + for (String name : BOLD_ITALIC_NAMES) { + if (fontName.contains(name)) return Font.BOLD | Font.ITALIC; + } + for (String name : ITALIC_NAMES) { + if (fontName.contains(name)) return Font.ITALIC; + } + for (String name : BOLD_NAMES) { + if (fontName.contains(name)) return Font.BOLD; + } + return Font.PLAIN; + } + private static Pair fontFamily(String familyName, int style) { if (SystemInfo.isMac && style > 0 && style < 4) { Pair[] replacement = ourStyledFontMap.get(familyName); From 957c648ee2d82bb4ca115be74280b838834d6596 Mon Sep 17 00:00:00 2001 From: Dennis Ushakov Date: Thu, 2 Jul 2015 10:53:59 +0200 Subject: [PATCH 062/137] change emmet edit points shortcut for old OSX keymap too #WEB-16977 fixed --- platform/platform-resources/src/META-INF/XmlActions.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platform/platform-resources/src/META-INF/XmlActions.xml b/platform/platform-resources/src/META-INF/XmlActions.xml index a7862132188f..8bbde4689a10 100644 --- a/platform/platform-resources/src/META-INF/XmlActions.xml +++ b/platform/platform-resources/src/META-INF/XmlActions.xml @@ -105,12 +105,14 @@ + + From f1306ccb3d044aaedc7c78ffd110d753a66a9a4f Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Thu, 2 Jul 2015 13:43:41 +0300 Subject: [PATCH 063/137] inline method to avoid potential garbage generation source (see IDEA-140802) --- .../impl/ComplementaryFontsRegistry.java | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/ComplementaryFontsRegistry.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/ComplementaryFontsRegistry.java index e1eab3ac56a9..eff05d5534f1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/ComplementaryFontsRegistry.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/ComplementaryFontsRegistry.java @@ -159,17 +159,6 @@ public class ComplementaryFontsRegistry { return Font.PLAIN; } - private static Pair fontFamily(String familyName, int style) { - if (SystemInfo.isMac && style > 0 && style < 4) { - Pair[] replacement = ourStyledFontMap.get(familyName); - if (replacement != null) { - familyName = replacement[style].first; - style = replacement[style].second; - } - } - return Pair.create(familyName, style); - } - @NotNull public static FontInfo getFontAbleToDisplay(char c, @JdkConstants.FontStyle int style, @NotNull FontPreferences preferences) { boolean tryDefaultFont = true; @@ -209,11 +198,17 @@ public class ComplementaryFontsRegistry { @Nullable private static FontInfo doGetFontAbleToDisplay(char c, int size, @JdkConstants.FontStyle int style, @NotNull String defaultFontFamily) { synchronized (lock) { - Pair p = fontFamily(defaultFontFamily, style); + if (SystemInfo.isMac && style > 0 && style < 4) { + Pair[] replacement = ourStyledFontMap.get(defaultFontFamily); + if (replacement != null) { + defaultFontFamily = replacement[style].first; + style = replacement[style].second; + } + } if (ourSharedKeyInstance.mySize == size && - ourSharedKeyInstance.myStyle == p.getSecond() && + ourSharedKeyInstance.myStyle == style && ourSharedKeyInstance.myFamilyName != null && - ourSharedKeyInstance.myFamilyName.equals(p.getFirst()) && + ourSharedKeyInstance.myFamilyName.equals(defaultFontFamily) && ourSharedDefaultFont != null && ( c < 128 || ourSharedDefaultFont.canDisplay(c) @@ -222,13 +217,13 @@ public class ComplementaryFontsRegistry { return ourSharedDefaultFont; } - ourSharedKeyInstance.myFamilyName = p.getFirst(); + ourSharedKeyInstance.myFamilyName = defaultFontFamily; ourSharedKeyInstance.mySize = size; - ourSharedKeyInstance.myStyle = p.getSecond(); + ourSharedKeyInstance.myStyle = style; FontInfo defaultFont = ourUsedFonts.get(ourSharedKeyInstance); if (defaultFont == null) { - defaultFont = new FontInfo(p.getFirst(), size, p.getSecond()); + defaultFont = new FontInfo(defaultFontFamily, size, style); ourUsedFonts.put(ourSharedKeyInstance, defaultFont); ourSharedKeyInstance = new FontKey("", 0, 0); } From c2500ca316b637ea6a421a9804188f557518401b Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Thu, 2 Jul 2015 12:48:41 +0200 Subject: [PATCH 064/137] fix tests, less garbage during index updates (IDEA-142179) --- .../psi/impl/cache/impl/IdCacheTest.java | 11 +-- .../intellij/psi/search/UpdateCacheTest.java | 11 +-- .../util/indexing/FileBasedIndexImpl.java | 96 +++++++++---------- 3 files changed, 53 insertions(+), 65 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/psi/impl/cache/impl/IdCacheTest.java b/java/java-tests/testSrc/com/intellij/psi/impl/cache/impl/IdCacheTest.java index 34a66d93c262..a1c4fbba4a23 100644 --- a/java/java-tests/testSrc/com/intellij/psi/impl/cache/impl/IdCacheTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/impl/cache/impl/IdCacheTest.java @@ -27,13 +27,13 @@ import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.psi.impl.cache.CacheManager; import com.intellij.psi.impl.cache.TodoCacheManager; -import com.intellij.psi.impl.cache.impl.id.IdIndex; -import com.intellij.psi.impl.cache.impl.todo.TodoIndex; -import com.intellij.psi.search.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.TodoAttributesUtil; +import com.intellij.psi.search.TodoPattern; +import com.intellij.psi.search.UsageSearchContext; import com.intellij.testFramework.IdeaTestUtil; import com.intellij.testFramework.PsiTestUtil; import com.intellij.util.ArrayUtil; -import com.intellij.util.indexing.FileBasedIndex; import java.io.File; import java.util.Arrays; @@ -48,9 +48,6 @@ public class IdCacheTest extends CodeInsightTestCase{ protected void setUp() throws Exception { super.setUp(); - FileBasedIndex.getInstance().requestRebuild(IdIndex.NAME); - FileBasedIndex.getInstance().requestRebuild(TodoIndex.NAME); - String root = JavaTestUtil.getJavaTestDataPath()+ "/psi/impl/cache/"; PsiTestUtil.removeAllRoots(myModule, IdeaTestUtil.getMockJdk17()); diff --git a/java/java-tests/testSrc/com/intellij/psi/search/UpdateCacheTest.java b/java/java-tests/testSrc/com/intellij/psi/search/UpdateCacheTest.java index 3a4f928505e7..14830b0a7402 100644 --- a/java/java-tests/testSrc/com/intellij/psi/search/UpdateCacheTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/search/UpdateCacheTest.java @@ -34,7 +34,6 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.impl.JavaPsiFacadeEx; import com.intellij.psi.impl.PsiManagerImpl; -import com.intellij.psi.impl.cache.impl.id.IdIndex; import com.intellij.psi.impl.cache.impl.todo.TodoIndex; import com.intellij.psi.impl.cache.impl.todo.TodoIndexEntry; import com.intellij.psi.impl.source.tree.injected.InjectedLanguageManagerImpl; @@ -52,15 +51,7 @@ import java.io.File; import java.util.*; @PlatformTestCase.WrapInCommand -public class UpdateCacheTest extends PsiTestCase{ - @Override - protected void setUp() throws Exception { - super.setUp(); - - FileBasedIndex.getInstance().requestRebuild(IdIndex.NAME); - FileBasedIndex.getInstance().requestRebuild(TodoIndex.NAME); - } - +public class UpdateCacheTest extends PsiTestCase { @Override protected void setUpProject() throws Exception { myProjectManager = ProjectManagerEx.getInstanceEx(); diff --git a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java index e327cf9e2d6f..93a4f5a0e133 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java @@ -1716,9 +1716,11 @@ public class FileBasedIndexImpl extends FileBasedIndex { fc.putUserData(IndexingDataKeys.PROJECT, project); } + static final Key ourPhysicalContentKey = Key.create("physical.content.flag"); + private void updateSingleIndex(@NotNull ID indexId, final int inputId, @Nullable FileContent currentFC) throws StorageException { - if (ourRebuildStatus.get(indexId).get() == REQUIRES_REBUILD) { + if (ourRebuildStatus.get(indexId).get() == REQUIRES_REBUILD && !myIsUnitTestMode) { return; // the index is scheduled for rebuild, no need to update } myLocalModCount++; @@ -1726,7 +1728,8 @@ public class FileBasedIndexImpl extends FileBasedIndex { final UpdatableIndex index = getIndex(indexId); assert index != null; - if (currentFC != null && currentFC.getUserData(ourPhysicalContentKey) == null) { + boolean hasContent = currentFC != null; + if (hasContent && currentFC.getUserData(ourPhysicalContentKey) == null) { currentFC.putUserData(ourPhysicalContentKey, Boolean.TRUE); } @@ -1735,10 +1738,7 @@ public class FileBasedIndexImpl extends FileBasedIndex { final Computable update = index.update(inputId, currentFC); try { - scheduleUpdate(indexId, - createUpdateComputableWithBufferingDisabled(update), - createIndexedStampUpdateRunnable(indexId, inputId, currentFC != null) - ); + scheduleUpdate(indexId, update, inputId, hasContent); } catch (RuntimeException exception) { Throwable causeToRebuildIndex = getCauseToRebuildIndex(exception); if (causeToRebuildIndex != null) { @@ -1750,51 +1750,51 @@ public class FileBasedIndexImpl extends FileBasedIndex { } } - static final Key ourPhysicalContentKey = Key.create("physical.content.flag"); - - @NotNull - private Runnable createIndexedStampUpdateRunnable(@NotNull final ID indexId, - final int fileId, - final boolean hasContent) { - return new Runnable() { - @Override - public void run() { - if (hasContent) { - IndexingStamp.setFileIndexedStateCurrent(fileId, indexId); - } - else { - IndexingStamp.setFileIndexedStateUnindexed(fileId, indexId); - } - if (myNotRequiringContentIndices.contains(indexId)) IndexingStamp.flushCache(fileId); - } - }; - } - - @NotNull - private Computable createUpdateComputableWithBufferingDisabled(@NotNull final Computable update) { - return new Computable() { - @Override - public Boolean compute() { - Boolean result; - final StorageGuard.StorageModeExitHandler lock = setDataBufferingEnabled(false); - try { - result = update.compute(); - } - finally { - lock.leave(); - } - return result; - } - }; - } - - private void scheduleUpdate(@NotNull ID indexId, @NotNull Computable update, @NotNull Runnable successRunnable) { + private void scheduleUpdate(@NotNull final ID indexId, final Computable update, final int inputId, final boolean hasContent) { if (myNotRequiringContentIndices.contains(indexId) && !Registry.is("idea.concurrent.scanning.files.to.index")) { - myContentlessIndicesUpdateQueue.submit(update, successRunnable); + myContentlessIndicesUpdateQueue.submit( + new Computable() { + @Override + public Boolean compute() { + return updateWithBufferingEnabled(update); + } + }, + new Runnable() { + @Override + public void run() { + indexedStampUpdate(indexId, inputId, hasContent); + } + }); } else { - Boolean result = update.compute(); - if (result == Boolean.TRUE) ApplicationManager.getApplication().runReadAction(successRunnable); + if (updateWithBufferingEnabled(update)) { + AccessToken accessToken = ReadAction.start(); + try { + indexedStampUpdate(indexId, inputId, hasContent); + } finally { + accessToken.finish(); + } + } + } + } + + protected void indexedStampUpdate(@NotNull ID indexId, int fileId, boolean hasContent) { + if (hasContent) { + IndexingStamp.setFileIndexedStateCurrent(fileId, indexId); + } + else { + IndexingStamp.setFileIndexedStateUnindexed(fileId, indexId); + } + if (myNotRequiringContentIndices.contains(indexId)) IndexingStamp.flushCache(fileId); + } + + protected boolean updateWithBufferingEnabled(@NotNull final Computable update) { + final StorageGuard.StorageModeExitHandler lock = setDataBufferingEnabled(false); + try { + return update.compute(); + } + finally { + lock.leave(); } } From ddb47d02614daf2037c7c0160ba738fa3d83831b Mon Sep 17 00:00:00 2001 From: "Vladimir.Orlov" Date: Thu, 2 Jul 2015 14:26:57 +0300 Subject: [PATCH 065/137] IDEA-142202 IDEA with bundled jdk doesn't start on Ubuntu --- bin/scripts/unix/idea.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/scripts/unix/idea.sh b/bin/scripts/unix/idea.sh index ca721e9082fd..b08d1d94c241 100755 --- a/bin/scripts/unix/idea.sh +++ b/bin/scripts/unix/idea.sh @@ -61,7 +61,7 @@ IDE_BIN_HOME=`dirname "$SCRIPT_LOCATION"` # --------------------------------------------------------------------- if [ -n "$@@product_uc@@_JDK" -a -x "$@@product_uc@@_JDK/bin/java" ]; then JDK="$@@product_uc@@_JDK" -elif [ -x "$IDE_HOME/jre/bin/java" ] && "$IDE_HOME/jre/bin/java" -version > /dev/null 2>&1 ; then +elif [ -x "$IDE_HOME/jre/jre/bin/java" ] && "$IDE_HOME/jre/jre/bin/java" -version > /dev/null 2>&1 ; then JDK="$IDE_HOME/jre" elif [ -n "$JDK_HOME" -a -x "$JDK_HOME/bin/java" ]; then JDK="$JDK_HOME" @@ -173,7 +173,7 @@ fi # --------------------------------------------------------------------- # Run the IDE. # --------------------------------------------------------------------- -LD_LIBRARY_PATH="$IDE_BIN_HOME:$LD_LIBRARY_PATH" "$JDK/bin/java" \ +LD_LIBRARY_PATH="$IDE_BIN_HOME:$LD_LIBRARY_PATH" "$JDK/jre/bin/java" \ $AGENT \ "-Xbootclasspath/a:$IDE_HOME/lib/boot.jar" \ -classpath "$CLASSPATH" \ From 6db30809b4ca5d9a79c67ef75c6b636bfd3ca0d8 Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Thu, 2 Jul 2015 13:16:19 +0200 Subject: [PATCH 066/137] FileUtil.toCanonicalPath can now expand symlinks when it's required to build the valid path that contains /../ --- .../intellij/openapi/util/io/FileUtil.java | 62 +++++++++++++++++-- .../openapi/util/io/FileUtilHeavyTest.java | 53 ++++++++++++++++ 2 files changed, 111 insertions(+), 4 deletions(-) diff --git a/platform/util/src/com/intellij/openapi/util/io/FileUtil.java b/platform/util/src/com/intellij/openapi/util/io/FileUtil.java index 7ad8343fc52d..e48f1b4d995f 100644 --- a/platform/util/src/com/intellij/openapi/util/io/FileUtil.java +++ b/platform/util/src/com/intellij/openapi/util/io/FileUtil.java @@ -645,13 +645,35 @@ public class FileUtil extends FileUtilRt { /** * Converts given path to canonical representation by eliminating '.'s, traversing '..'s, and omitting duplicate separators. * Please note that this method is symlink-unfriendly (i.e. result of "/path/to/link/../next" most probably will differ from - * what {@link java.io.File#getCanonicalPath()} will return) - so use with care. + * what {@link java.io.File#getCanonicalPath()} will return) - so use with care.
+ *
+ * If the path may contain synlinks, use use {@link FileUtil#toCanonicalPath(String, boolean)} */ @Contract("null -> null") public static String toCanonicalPath(@Nullable String path) { return toCanonicalPath(path, File.separatorChar, true); } + /** + * When relative ../ parts do not escape outside of symlinks, the links are not expanded.
+ * That is, in the best-case scenario the original non-expanded path is preserved.
+ *
+ * Otherwise, returns a fully resolved path using {@link java.io.File#getCanonicalPath()}.
+ *
+ * Consider the following case: + *
+   * root/
+   *   dir1/
+   *     link_to_dir1
+   *   dir2/
+   * 
+ * 'root/dir1/link_to_dir1/../dir2' should be resolved to 'root/dir2' + */ + @Contract("null, _ -> null") + public static String toCanonicalPath(@Nullable String path, boolean resolveSymlinksIfNecessary) { + return toCanonicalPath(path, File.separatorChar, true, resolveSymlinksIfNecessary); + } + @Contract("null, _ -> null") public static String toCanonicalPath(@Nullable String path, char separatorChar) { return toCanonicalPath(path, separatorChar, true); @@ -664,6 +686,14 @@ public class FileUtil extends FileUtilRt { @Contract("null, _, _ -> null") private static String toCanonicalPath(@Nullable String path, char separatorChar, boolean removeLastSlash) { + return toCanonicalPath(path, separatorChar, removeLastSlash, false); + } + + @Contract("null, _, _, _ -> null") + private static String toCanonicalPath(@Nullable String path, + final char separatorChar, + final boolean removeLastSlash, + final boolean resolveSymlinksIfNecessary) { if (path == null || path.isEmpty()) { return path; } @@ -676,6 +706,21 @@ public class FileUtil extends FileUtilRt { return path; } + final String finalPath = path; + NotNullProducer realCanonicalPath = !resolveSymlinksIfNecessary ? null : new NotNullProducer() { + @NotNull + @Override + public String produce() { + try { + return new File(finalPath).getCanonicalPath().replace(separatorChar, '/'); + } + catch (IOException ignore) { + // fall back to the default behavior + return toCanonicalPath(finalPath, separatorChar, removeLastSlash, false); + } + } + }; + StringBuilder result = new StringBuilder(path.length()); int start = processRoot(path, result); int dots = 0; @@ -685,7 +730,9 @@ public class FileUtil extends FileUtilRt { char c = path.charAt(i); if (c == '/') { if (!separator) { - processDots(result, dots, start); + if (!processDots(result, dots, start, resolveSymlinksIfNecessary)) { + return realCanonicalPath.produce(); + } dots = 0; } separator = true; @@ -710,7 +757,9 @@ public class FileUtil extends FileUtilRt { } if (dots > 0) { - processDots(result, dots, start); + if (!processDots(result, dots, start, resolveSymlinksIfNecessary)) { + return realCanonicalPath.produce(); + } } int lastChar = result.length() - 1; @@ -754,7 +803,8 @@ public class FileUtil extends FileUtilRt { return 0; } - private static void processDots(@NotNull StringBuilder result, int dots, int start) { + @Contract("_, _, _, false -> true") + private static boolean processDots(@NotNull StringBuilder result, int dots, int start, boolean resolveSymlinksIfNecessary) { if (dots == 2) { int pos = -1; if (!StringUtil.endsWith(result, "/../") && !StringUtil.equals(result, "../")) { @@ -770,6 +820,9 @@ public class FileUtil extends FileUtilRt { } } if (pos >= 0) { + if (resolveSymlinksIfNecessary && FileSystemUtil.isSymLink(new File(result.toString()))) { + return false; + } result.delete(pos, result.length()); } else { @@ -780,6 +833,7 @@ public class FileUtil extends FileUtilRt { StringUtil.repeatSymbol(result, '.', dots); result.append('/'); } + return true; } /** diff --git a/platform/util/testSrc/com/intellij/openapi/util/io/FileUtilHeavyTest.java b/platform/util/testSrc/com/intellij/openapi/util/io/FileUtilHeavyTest.java index bd6c32bfb57e..0e118cab81bb 100644 --- a/platform/util/testSrc/com/intellij/openapi/util/io/FileUtilHeavyTest.java +++ b/platform/util/testSrc/com/intellij/openapi/util/io/FileUtilHeavyTest.java @@ -227,6 +227,59 @@ public class FileUtilHeavyTest { FileUtil.delete(linkDir); assertEquals(1, targetDir.list().length); } + + + @Test + public void testToCanonicalPathSymLinksAware() throws Exception { + assumeTrue(SystemInfo.areSymLinksSupported); + + File root = IoTestUtil.createTestDir(myTempDirectory, "root"); + assertTrue(new File(root, "dir1/dir2/dir3/dir4").mkdirs()); + + // non-recursive link + IoTestUtil.createSymLink(new File(root, "dir1/dir2").getPath(), new File(root, "dir1/dir2_link").getPath()); + // recursive links to a parent dir + IoTestUtil.createSymLink(new File(root, "dir1").getPath(), new File(root, "dir1/dir1_link").getPath()); + + // I) links should NOT be resolved when ../ stays inside the linked path + // I.I) non-recursive links + assertEquals(root + "/dir1/dir2_link", FileUtil.toCanonicalPath(root + "/dir1/dir2_link/./", true)); + assertEquals(root + "/dir1/dir2_link", FileUtil.toCanonicalPath(root + "/dir1/dir2_link/dir3/../", true)); + assertEquals(root + "/dir1/dir2_link/dir3", FileUtil.toCanonicalPath(root + "/dir1/dir2_link/dir3/dir4/../", true)); + assertEquals(root + "/dir1/dir2_link", FileUtil.toCanonicalPath(root + "/dir1/dir2_link/dir3/dir4/../../", true)); + assertEquals(root + "/dir1/dir2_link", FileUtil.toCanonicalPath(root + "/dir1/../dir1/dir2_link/dir3/../", true)); + + // I.II) recursive links + assertEquals(root + "/dir1/dir1_link", FileUtil.toCanonicalPath(root + "/dir1/dir1_link/./", true)); + assertEquals(root + "/dir1/dir1_link", FileUtil.toCanonicalPath(root + "/dir1/dir1_link/dir2/../", true)); + assertEquals(root + "/dir1/dir1_link/dir2", FileUtil.toCanonicalPath(root + "/dir1/dir1_link/dir2/dir3/../", true)); + assertEquals(root + "/dir1/dir1_link", FileUtil.toCanonicalPath(root + "/dir1/dir1_link/dir2/dir3/../../", true)); + assertEquals(root + "/dir1/dir1_link", FileUtil.toCanonicalPath(root + "/dir1/../dir1/dir1_link/dir2/../", true)); + + // II) links should be resolved is ../ escapes outside + + // II.I) non-recursive links + assertEquals(root + "/dir1", FileUtil.toCanonicalPath(root + "/dir1/dir2_link/../", true)); + assertEquals(root + "/dir1/dir2", FileUtil.toCanonicalPath(root + "/dir1/dir2_link/../dir2", true)); + assertEquals(root + "/dir1/dir2", FileUtil.toCanonicalPath(root + "/dir1/dir2_link/../../dir1/dir2", true)); + assertEquals(root + "/dir1/dir2", FileUtil.toCanonicalPath(root + "/dir1/dir2_link/dir3/../../dir2", true)); + assertEquals(root + "/dir1/dir2", FileUtil.toCanonicalPath(root + "/dir1/dir2_link/dir3/../../../dir1/dir2", true)); + assertEquals(root + "/dir1/dir2", FileUtil.toCanonicalPath(root + "/dir1/../dir1/dir2_link/../dir2", true)); + + // II.I) recursive links + assertEquals(root.getPath(), FileUtil.toCanonicalPath(root + "/dir1/dir1_link/../", true)); + assertEquals(root + "/dir1", FileUtil.toCanonicalPath(root + "/dir1/dir1_link/../dir1", true)); + assertEquals(root + "/dir1", FileUtil.toCanonicalPath(root + "/dir1/dir1_link/../../root/dir1", true)); + assertEquals(root + "/dir1", FileUtil.toCanonicalPath(root + "/dir1/dir1_link/dir3/../../dir1", true)); + assertEquals(root + "/dir1", FileUtil.toCanonicalPath(root + "/dir1/dir1_link/dir3/../../../root/dir1", true)); + assertEquals(root + "/dir1", FileUtil.toCanonicalPath(root + "/dir1/../dir1/dir1_link/../dir1", true)); + + // some corner cases, behavior should be the same as the default FileUtil.toCanonicalPath + assertEquals(FileUtil.toCanonicalPath("..", false), FileUtil.toCanonicalPath("..", true)); + assertEquals(FileUtil.toCanonicalPath("../", false), FileUtil.toCanonicalPath("../", true)); + assertEquals(FileUtil.toCanonicalPath("/..", false), FileUtil.toCanonicalPath("/..", true)); + assertEquals(FileUtil.toCanonicalPath("/../", false), FileUtil.toCanonicalPath("/../", true)); + } @Test public void testCaseSensitivityDetection() throws IOException { From 66029598c260dfcce56cc24fc008af17c1a81a56 Mon Sep 17 00:00:00 2001 From: "Vladimir.Orlov" Date: Thu, 2 Jul 2015 15:07:08 +0300 Subject: [PATCH 067/137] PyCharmPY and PC jdk bundled linux artifact. --- python/build/pycharm_community_build.gant | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/build/pycharm_community_build.gant b/python/build/pycharm_community_build.gant index 997c151df8c0..b1daea58a3a3 100644 --- a/python/build/pycharm_community_build.gant +++ b/python/build/pycharm_community_build.gant @@ -176,6 +176,9 @@ public layoutCommunity(String classesPath, Set usedJars) { String tarRoot = isEap() ? "pycharm-community-$buildNumber" : "pycharm-community-${p("component.version.major")}.${p("component.version.minor")}" buildTarGz(tarRoot, "$paths.artifacts/pycharmPC-${buildNumber}.tar", [paths.distAll, paths.distUnix]) + if (p("jdk.bundled.linux") != "false") { + buildTarGz(tarRoot, "$paths.artifacts/pycharmPC-${buildName}-jdk-bundled.tar", [paths.distAll, paths.distUnix, "${home}/out/pycharm/bundled.linux.jdk"], ["jre/jre/bin/*"]) + } String macAppRoot = isEap() ? "PyCharm CE ${p("component.version.major")}.${p("component.version.minor")} EAP.app/Contents" : "PyCharm CE.app/Contents" buildMacZip(macAppRoot, "${paths.artifacts}/pycharmPC-${buildNumber}.sit", [paths.distAll], paths.distMac) From 1fcdb2ab9cabde1d90f1582ec5f1ebe51a24fd55 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Wed, 1 Jul 2015 09:59:47 +0200 Subject: [PATCH 068/137] correct javadoc --- .../util/xmlb/SkipDefaultValuesSerializationFilters.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/util/src/com/intellij/util/xmlb/SkipDefaultValuesSerializationFilters.java b/platform/util/src/com/intellij/util/xmlb/SkipDefaultValuesSerializationFilters.java index 6bc5f00ab044..39e4cd83abe5 100644 --- a/platform/util/src/com/intellij/util/xmlb/SkipDefaultValuesSerializationFilters.java +++ b/platform/util/src/com/intellij/util/xmlb/SkipDefaultValuesSerializationFilters.java @@ -26,7 +26,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Map; /** - * Please use {@link SkipDefaultsSerializationFilter}, + * Please use {@link SkipDefaultsSerializationFilter} if state class doesn't implement "equals" (in Kotlin use data classes {@see http://kotlinlang.org/docs/reference/data-classes.html}) */ public class SkipDefaultValuesSerializationFilters extends SerializationFilterBase { private final Map, Object> myDefaultBeans = new THashMap, Object>(); From 78c855b8f5994d0ec43aaaa575d2fc6b6b98a9eb Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Wed, 1 Jul 2015 10:03:06 +0200 Subject: [PATCH 069/137] hamcrest FileExistenceMatcher, junit 4 TemporaryDirectory --- lib/org/hamcrest/annotations.xml | 11 ++++++ .../testSrc/TemporaryDirectory.kt | 39 +++++++++++++++++++ platform/testFramework/testSrc/matchers.kt | 39 +++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 lib/org/hamcrest/annotations.xml create mode 100644 platform/testFramework/testSrc/TemporaryDirectory.kt create mode 100644 platform/testFramework/testSrc/matchers.kt diff --git a/lib/org/hamcrest/annotations.xml b/lib/org/hamcrest/annotations.xml new file mode 100644 index 000000000000..36d1cf4640d1 --- /dev/null +++ b/lib/org/hamcrest/annotations.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/platform/testFramework/testSrc/TemporaryDirectory.kt b/platform/testFramework/testSrc/TemporaryDirectory.kt new file mode 100644 index 000000000000..d49ca2587174 --- /dev/null +++ b/platform/testFramework/testSrc/TemporaryDirectory.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.testFramework + +import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.util.io.FileUtilRt +import com.intellij.util.SmartList +import org.junit.rules.ExternalResource +import java.io.File + +public class TemporaryDirectory : ExternalResource() { + private val files = SmartList() + + override fun after() { + for (file in files) { + FileUtil.delete(file) + } + files.clear() + } + + public fun newDirectory(): File { + val file = FileUtilRt.generateRandomTemporaryPath() + files.add(file) + return file; + } +} diff --git a/platform/testFramework/testSrc/matchers.kt b/platform/testFramework/testSrc/matchers.kt new file mode 100644 index 000000000000..7f2fb364bba4 --- /dev/null +++ b/platform/testFramework/testSrc/matchers.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.testFramework + +import org.hamcrest.Description +import org.hamcrest.Factory +import org.hamcrest.Matcher +import org.hamcrest.TypeSafeDiagnosingMatcher +import java.io.File + +public class FileExistenceMatcher(private val exists: Boolean) : TypeSafeDiagnosingMatcher(javaClass()) { + override fun matchesSafely(file: File, mismatchDescription: Description): Boolean { + if (exists == file.exists()) { + return true + } + + mismatchDescription.appendText("is not a file") + return false + } + + override fun describeTo(description: Description) { + description.appendText("an existing file") + } +} + +public Factory fun exists(): Matcher = FileExistenceMatcher(true) \ No newline at end of file From ee54d5f5c8074183cd5ee140ba5d1e52e717d350 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Wed, 1 Jul 2015 12:00:07 +0200 Subject: [PATCH 070/137] list model editor: respect isRemovable --- .../intellij/util/ui/CollectionModelEditor.java | 17 +++++++++++++++++ .../com/intellij/util/ui/ListModelEditor.java | 11 +++++++++-- .../util/ui/table/TableModelEditor.java | 7 +------ 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/util/ui/CollectionModelEditor.java b/platform/platform-impl/src/com/intellij/util/ui/CollectionModelEditor.java index b3e0cbd6c396..395327aa653c 100644 --- a/platform/platform-impl/src/com/intellij/util/ui/CollectionModelEditor.java +++ b/platform/platform-impl/src/com/intellij/util/ui/CollectionModelEditor.java @@ -25,6 +25,7 @@ import gnu.trove.TObjectObjectProcedure; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import javax.swing.*; import java.util.List; public abstract class CollectionModelEditor> implements ElementProducer { @@ -146,4 +147,20 @@ public abstract class CollectionModelEditor modifiedToOriginal.forEachEntry(procedure); } } + + protected final boolean areSelectedItemsRemovable(@NotNull ListSelectionModel selectionMode) { + int minSelectionIndex = selectionMode.getMinSelectionIndex(); + int maxSelectionIndex = selectionMode.getMaxSelectionIndex(); + if (minSelectionIndex < 0 || maxSelectionIndex < 0) { + return false; + } + + List items = getItems(); + for (int i = minSelectionIndex; i <= maxSelectionIndex; i++) { + if (itemEditor.isRemovable(items.get(i))) { + return true; + } + } + return false; + } } \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/util/ui/ListModelEditor.java b/platform/platform-impl/src/com/intellij/util/ui/ListModelEditor.java index 42150a78dcc0..ba9208c3ff91 100644 --- a/platform/platform-impl/src/com/intellij/util/ui/ListModelEditor.java +++ b/platform/platform-impl/src/com/intellij/util/ui/ListModelEditor.java @@ -15,6 +15,7 @@ */ package com.intellij.util.ui; +import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.ui.*; import com.intellij.ui.components.JBList; import com.intellij.util.containers.ContainerUtil; @@ -35,7 +36,7 @@ public class ListModelEditor extends ListModelEditorBase { list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); list.setCellRenderer(new MyListCellRenderer()); - toolbarDecorator = ToolbarDecorator.createDecorator(list) + toolbarDecorator = ToolbarDecorator.createDecorator(list, model) .setAddAction(new AnActionButtonRunnable() { @Override public void run(AnActionButton button) { @@ -51,7 +52,13 @@ public class ListModelEditor extends ListModelEditorBase { model.add(item); ListScrollingUtil.selectItem(list, ContainerUtil.indexOfIdentity(model.getItems(), item)); } - }); + }) + .setRemoveActionUpdater(new AnActionButtonUpdater() { + @Override + public boolean isEnabled(AnActionEvent e) { + return areSelectedItemsRemovable(list.getSelectionModel()); + } + }); } @NotNull diff --git a/platform/platform-impl/src/com/intellij/util/ui/table/TableModelEditor.java b/platform/platform-impl/src/com/intellij/util/ui/table/TableModelEditor.java index 5d881b9e1777..8af243e1d817 100644 --- a/platform/platform-impl/src/com/intellij/util/ui/table/TableModelEditor.java +++ b/platform/platform-impl/src/com/intellij/util/ui/table/TableModelEditor.java @@ -365,12 +365,7 @@ public class TableModelEditor extends CollectionModelEditor Date: Wed, 1 Jul 2015 12:48:17 +0200 Subject: [PATCH 071/137] prepare to convert SchemeManagerImpl to kotlin --- .../testSrc/BinaryRequestHandlerTest.kt | 1 + .../built-in-server/testSrc/RestApiTest.kt | 18 ++++++------- .../built-in-server/testSrc/TestManager.kt | 14 +++++----- platform/core-impl/core-impl.iml | 1 + .../intellij/openapi/application/actions.kt | 26 +++++++++++++++++++ platform/testFramework/testSrc/FixtureRule.kt | 13 +--------- 6 files changed, 46 insertions(+), 27 deletions(-) create mode 100644 platform/core-impl/src/com/intellij/openapi/application/actions.kt diff --git a/platform/built-in-server/testSrc/BinaryRequestHandlerTest.kt b/platform/built-in-server/testSrc/BinaryRequestHandlerTest.kt index 0c8c4dc5e13f..1d3b65cc977f 100644 --- a/platform/built-in-server/testSrc/BinaryRequestHandlerTest.kt +++ b/platform/built-in-server/testSrc/BinaryRequestHandlerTest.kt @@ -17,6 +17,7 @@ import org.jetbrains.io.ChannelExceptionHandler import org.jetbrains.io.Decoder import org.jetbrains.io.MessageDecoder import org.jetbrains.io.NettyUtil +import org.jetbrains.testFramework.FixtureRule import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain diff --git a/platform/built-in-server/testSrc/RestApiTest.kt b/platform/built-in-server/testSrc/RestApiTest.kt index 2f9586857102..b4716ebfac5e 100644 --- a/platform/built-in-server/testSrc/RestApiTest.kt +++ b/platform/built-in-server/testSrc/RestApiTest.kt @@ -1,19 +1,19 @@ package org.jetbrains.ide +import com.google.gson.stream.JsonWriter +import com.intellij.openapi.vfs.CharsetToolkit import io.netty.handler.codec.http.HttpResponseStatus +import org.hamcrest.CoreMatchers.equalTo +import org.jetbrains.ide.TestManager.TestDescriptor +import org.jetbrains.testFramework.FixtureRule +import org.junit.Assert.assertThat import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain - -import org.hamcrest.CoreMatchers.equalTo -import org.jetbrains.ide.TestManager.TestDescriptor -import org.junit.Assert.assertThat -import java.net.URL -import java.net.HttpURLConnection -import com.google.gson.stream.JsonWriter -import java.io.OutputStreamWriter -import com.intellij.openapi.vfs.CharsetToolkit import java.io.BufferedOutputStream +import java.io.OutputStreamWriter +import java.net.HttpURLConnection +import java.net.URL public class RestApiTest { private val fixtureManager = FixtureRule() diff --git a/platform/built-in-server/testSrc/TestManager.kt b/platform/built-in-server/testSrc/TestManager.kt index 50fa6cd3fe84..05f13c915c89 100644 --- a/platform/built-in-server/testSrc/TestManager.kt +++ b/platform/built-in-server/testSrc/TestManager.kt @@ -1,11 +1,13 @@ package org.jetbrains.ide +import com.intellij.openapi.application.writeAction import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.roots.ModuleRootModificationUtil import com.intellij.openapi.util.io.FileUtilRt import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.vfs.VirtualFile import com.intellij.testFramework.fixtures.IdeaProjectTestFixture +import org.jetbrains.testFramework.invokeAndWaitIfNeed import org.junit.rules.TestWatcher import org.junit.runner.Description import java.io.File @@ -63,10 +65,10 @@ class TestManager(val projectFixture: IdeaProjectTestFixture) : TestWatcher() { } invokeAndWaitIfNeed { - val normalizedFilePath = FileUtilRt.toSystemIndependentName(filePath) + val normalizedFilePath = FileUtilRt.toSystemIndependentName(filePath!!) if (annotation!!.relativeToProject) { val root = projectFixture.getProject().getBaseDir() - runWriteAction { + writeAction { fileToDelete = root.findOrCreateChildData(this@TestManager, normalizedFilePath) } } @@ -75,7 +77,7 @@ class TestManager(val projectFixture: IdeaProjectTestFixture) : TestWatcher() { ModuleRootModificationUtil.updateModel(projectFixture.getModule()) { model -> val contentEntry = model.getContentEntries()[0] val contentRoot = contentEntry.getFile()!! - runWriteAction { + writeAction { contentRoot.findChild(EXCLUDED_DIR_NAME)?.delete(this@TestManager) fileToDelete = contentRoot.createChildDirectory(this@TestManager, EXCLUDED_DIR_NAME) fileToDelete!!.createChildData(this@TestManager, normalizedFilePath) @@ -87,7 +89,7 @@ class TestManager(val projectFixture: IdeaProjectTestFixture) : TestWatcher() { } else { val root = ModuleRootManager.getInstance(projectFixture.getModule()).getSourceRoots()[0] - runWriteAction { + writeAction { fileToDelete = root.findOrCreateChildData(this@TestManager, normalizedFilePath) } } @@ -101,11 +103,11 @@ class TestManager(val projectFixture: IdeaProjectTestFixture) : TestWatcher() { } if (fileToDelete != null) { - invokeAndWaitIfNeed { runWriteAction { fileToDelete?.delete(this@TestManager) } } + invokeAndWaitIfNeed { writeAction { fileToDelete?.delete(this@TestManager) } } fileToDelete = null } - if (ioFileToDelete != null && !FileUtilRt.delete(ioFileToDelete)) { + if (ioFileToDelete != null && !FileUtilRt.delete(ioFileToDelete!!)) { ioFileToDelete!!.deleteOnExit() } diff --git a/platform/core-impl/core-impl.iml b/platform/core-impl/core-impl.iml index 6d93fd99a32c..e57a2b6e112a 100644 --- a/platform/core-impl/core-impl.iml +++ b/platform/core-impl/core-impl.iml @@ -11,5 +11,6 @@ + \ No newline at end of file diff --git a/platform/core-impl/src/com/intellij/openapi/application/actions.kt b/platform/core-impl/src/com/intellij/openapi/application/actions.kt new file mode 100644 index 000000000000..df6f89b514bc --- /dev/null +++ b/platform/core-impl/src/com/intellij/openapi/application/actions.kt @@ -0,0 +1,26 @@ +/* + * 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.application + +public inline fun writeAction(runnable: () -> Unit) { + val token = WriteAction.start() + try { + runnable() + } + finally { + token.finish() + } +} \ No newline at end of file diff --git a/platform/testFramework/testSrc/FixtureRule.kt b/platform/testFramework/testSrc/FixtureRule.kt index 9edd6647a696..dcca0ef2d24c 100644 --- a/platform/testFramework/testSrc/FixtureRule.kt +++ b/platform/testFramework/testSrc/FixtureRule.kt @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.ide +package org.jetbrains.testFramework -import com.intellij.openapi.application.WriteAction import com.intellij.testFramework.UsefulTestCase import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory import org.junit.rules.ExternalResource @@ -25,16 +24,6 @@ public fun invokeAndWaitIfNeed(runnable: () -> Unit) { if (SwingUtilities.isEventDispatchThread()) runnable() else SwingUtilities.invokeAndWait(runnable) } -public inline fun runWriteAction(runnable: () -> Unit) { - val token = WriteAction.start() - try { - runnable() - } - finally { - token.finish() - } -} - public class FixtureRule() : ExternalResource() { val projectFixture = IdeaTestFixtureFactory.getFixtureFactory().createLightFixtureBuilder().getFixture() From ebf19fad9fbd95c935983a5b68381576a5c46780 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Wed, 1 Jul 2015 14:39:08 +0200 Subject: [PATCH 072/137] bring back SchemeManagerImpl to kotlin disable bundled scheme metadata editing --- .../openapi/options/SchemesManager.java | 12 +- .../openapi/options/EmptySchemesManager.java | 4 - platform/platform-impl/platform-impl.iml | 1 + .../actionSystem/ex/QuickListsManager.java | 20 +- .../impl/stores/StreamProvider.java | 89 -- .../components/impl/stores/StreamProvider.kt | 72 ++ .../keymap/impl/ui/QuickListPanel.form | 2 +- .../keymap/impl/ui/QuickListPanel.java | 22 +- .../keymap/impl/ui/QuickListsPanel.java | 2 +- .../openapi/keymap/impl/ui/QuickListsUi.java | 12 +- .../options/SchemesManagerFactoryImpl.java | 9 +- .../openapi/options/SchemesManagerImpl.java | 949 ------------------ .../openapi/options/SchemesManagerImpl.kt | 830 +++++++++++++++ .../com/intellij/util/ui/ListModelEditor.java | 10 +- .../util/ui/table/TableModelEditor.java | 3 + platform/platform-tests/platform-tests.iml | 1 + .../components/impl/ApplicationStoreTest.java | 288 ------ .../components/impl/ApplicationStoreTest.kt | 225 +++++ 18 files changed, 1181 insertions(+), 1370 deletions(-) delete mode 100644 platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.java create mode 100644 platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.kt delete mode 100644 platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.java create mode 100644 platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.kt delete mode 100644 platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ApplicationStoreTest.java create mode 100644 platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ApplicationStoreTest.kt diff --git a/platform/core-api/src/com/intellij/openapi/options/SchemesManager.java b/platform/core-api/src/com/intellij/openapi/options/SchemesManager.java index 88f6e37ce838..28844ab00844 100644 --- a/platform/core-api/src/com/intellij/openapi/options/SchemesManager.java +++ b/platform/core-api/src/com/intellij/openapi/options/SchemesManager.java @@ -46,8 +46,6 @@ public abstract class SchemesManager convertor) { } @@ -69,4 +70,11 @@ public abstract class SchemesManager schemes, @Nullable Condition removeCondition) { } + + /** + * Bundled / read-only (or overriding) scheme cannot be renamed or deleted. + */ + public boolean isMetadataEditable(@NotNull E scheme) { + return true; + } } diff --git a/platform/core-impl/src/com/intellij/openapi/options/EmptySchemesManager.java b/platform/core-impl/src/com/intellij/openapi/options/EmptySchemesManager.java index 55a74d08ebd1..f5bc6cee0c35 100644 --- a/platform/core-impl/src/com/intellij/openapi/options/EmptySchemesManager.java +++ b/platform/core-impl/src/com/intellij/openapi/options/EmptySchemesManager.java @@ -48,10 +48,6 @@ public class EmptySchemesManager extends SchemesManager { return null; } - @Override - public void save() { - } - @Override public void setCurrentSchemeName(String schemeName) { } diff --git a/platform/platform-impl/platform-impl.iml b/platform/platform-impl/platform-impl.iml index 64dc847f675a..b6c974e918c2 100644 --- a/platform/platform-impl/platform-impl.iml +++ b/platform/platform-impl/platform-impl.iml @@ -42,5 +42,6 @@ + \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/ex/QuickListsManager.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/ex/QuickListsManager.java index 8f8b0789f240..7cd47436a47b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/ex/QuickListsManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/ex/QuickListsManager.java @@ -43,11 +43,11 @@ public class QuickListsManager implements ExportableApplicationComponent { private static final String LIST_TAG = "list"; private final ActionManager myActionManager; - private final SchemesManager mySchemesManager; + private final SchemesManager mySchemeManager; public QuickListsManager(@NotNull ActionManager actionManager, @NotNull SchemesManagerFactory schemesManagerFactory) { myActionManager = actionManager; - mySchemesManager = schemesManagerFactory.createSchemesManager(FILE_SPEC, + mySchemeManager = schemesManagerFactory.createSchemesManager(FILE_SPEC, new BaseSchemeProcessor() { @NotNull @Override @@ -73,7 +73,7 @@ public class QuickListsManager implements ExportableApplicationComponent { @Override @NotNull public File[] getExportFiles() { - return new File[]{mySchemesManager.getRootDirectory()}; + return new File[]{mySchemeManager.getRootDirectory()}; } @NotNull @@ -99,7 +99,7 @@ public class QuickListsManager implements ExportableApplicationComponent { public void initComponent() { for (BundledQuickListsProvider provider : BundledQuickListsProvider.EP_NAME.getExtensions()) { for (final String path : provider.getBundledListsRelativePaths()) { - mySchemesManager.loadBundledScheme(path, provider, new ThrowableConvertor() { + mySchemeManager.loadBundledScheme(path, provider, new ThrowableConvertor() { @Override public QuickList convert(Element element) throws Throwable { return createItem(element); @@ -107,7 +107,7 @@ public class QuickListsManager implements ExportableApplicationComponent { }); } } - mySchemesManager.loadSchemes(); + mySchemeManager.loadSchemes(); registerActions(); } @@ -116,20 +116,20 @@ public class QuickListsManager implements ExportableApplicationComponent { } @NotNull - public List getQuickLists() { - return mySchemesManager.getAllSchemes(); + public SchemesManager getSchemeManager() { + return mySchemeManager; } @NotNull public QuickList[] getAllQuickLists() { - Collection lists = mySchemesManager.getAllSchemes(); + Collection lists = mySchemeManager.getAllSchemes(); return lists.toArray(new QuickList[lists.size()]); } private void registerActions() { // to prevent exception if 2 or more targets have the same name Set registeredIds = new THashSet(); - for (QuickList list : mySchemesManager.getAllSchemes()) { + for (QuickList list : mySchemeManager.getAllSchemes()) { String actionId = list.getActionId(); if (registeredIds.add(actionId)) { myActionManager.registerAction(actionId, new InvokeQuickListAction(list)); @@ -145,7 +145,7 @@ public class QuickListsManager implements ExportableApplicationComponent { public void setQuickLists(@NotNull List quickLists) { unregisterActions(); - mySchemesManager.setSchemes(quickLists); + mySchemeManager.setSchemes(quickLists); registerActions(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.java deleted file mode 100644 index 4b455372bbf1..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.java +++ /dev/null @@ -1,89 +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.components.impl.stores; - -import com.intellij.openapi.components.RoamingType; -import com.intellij.openapi.util.Condition; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Collection; -import java.util.Collections; - -public abstract class StreamProvider { - public static final StreamProvider[] EMPTY_ARRAY = new StreamProvider[0]; - - public boolean isEnabled() { - return true; - } - - /** - * fileSpec Only main fileSpec, not version - */ - public boolean isApplicable(@NotNull String fileSpec, @NotNull RoamingType roamingType) { - return true; - } - - /** - * @param fileSpec - * @param content bytes of content, size of array is not actual size of data, you must use {@code size} - * @param size actual size of data - */ - public abstract void saveContent(@NotNull String fileSpec, @NotNull byte[] content, int size, @NotNull RoamingType roamingType) throws IOException; - - @Nullable - public abstract InputStream loadContent(@NotNull String fileSpec, @NotNull RoamingType roamingType) throws IOException; - - @NotNull - public Collection listSubFiles(@NotNull String fileSpec, @NotNull RoamingType roamingType) { - return Collections.emptyList(); - } - - /** - * You must close passed input stream. - */ - public void processChildren(@NotNull String path, @NotNull RoamingType roamingType, @NotNull Condition filter, @NotNull ChildrenProcessor processor) { - for (String name : listSubFiles(path, roamingType)) { - if (!filter.value(name)) { - continue; - } - - InputStream input; - try { - input = loadContent(path + '/' + name, roamingType); - } - catch (IOException e) { - StorageUtil.LOG.error(e); - continue; - } - - if (input != null && !processor.process(name, input)) { - break; - } - } - } - - public abstract static class ChildrenProcessor { - public abstract boolean process(@NotNull String name, @NotNull InputStream input); - } - - /** - * Delete file or directory - */ - public abstract void delete(@NotNull String fileSpec, @NotNull RoamingType roamingType); -} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.kt b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.kt new file mode 100644 index 000000000000..86a31f2185bc --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.kt @@ -0,0 +1,72 @@ +/* + * 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.components.impl.stores + +import com.intellij.openapi.components.RoamingType +import java.io.IOException +import java.io.InputStream + +public interface StreamProvider { + public open fun isEnabled(): Boolean = true + + /** + * fileSpec Only main fileSpec, not version + */ + public open fun isApplicable(fileSpec: String, roamingType: RoamingType): Boolean = true + + /** + * @param fileSpec + * * + * @param content bytes of content, size of array is not actual size of data, you must use `size` + * * + * @param size actual size of data + */ + public fun saveContent(fileSpec: String, content: ByteArray, size: Int, roamingType: RoamingType) + + public fun loadContent(fileSpec: String, roamingType: RoamingType): InputStream? + + public open fun listSubFiles(fileSpec: String, roamingType: RoamingType): Collection = emptyList() + + /** + * You must close passed input stream. + */ + public open fun processChildren(path: String, roamingType: RoamingType, filter: (name: String) -> Boolean, processor: (name: String, input: InputStream) -> Boolean) { + for (name in listSubFiles(path, roamingType)) { + if (!filter(name)) { + continue + } + + val input: InputStream? + try { + input = loadContent("$path/$name", roamingType) + } + catch (e: IOException) { + StorageUtil.LOG.error(e) + continue + } + + + if (input != null && !processor(name, input)) { + break + } + } + } + + /** + * Delete file or directory + */ + public fun delete(fileSpec: String, roamingType: RoamingType) +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListPanel.form b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListPanel.form index bb2584ca890d..b19432465dff 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListPanel.form +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListPanel.form @@ -32,7 +32,7 @@ - + diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListPanel.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListPanel.java index 666f9e74b657..27d2e6732741 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListPanel.java @@ -20,6 +20,7 @@ import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.ex.QuickList; +import com.intellij.openapi.actionSystem.ex.QuickListsManager; import com.intellij.openapi.keymap.KeyMapBundle; import com.intellij.openapi.keymap.KeymapManager; import com.intellij.ui.*; @@ -36,7 +37,7 @@ class QuickListPanel { private final CollectionListModel actionsModel; private JPanel myPanel; private final JBList myActionsList; - JTextField myDisplayName; + JTextField myName; private JTextField myDescription; private JPanel myListPanel; QuickList item; @@ -97,10 +98,10 @@ class QuickListPanel { return; } - item.setName(myDisplayName.getText().trim()); + item.setName(myName.getText().trim()); item.setDescription(myDescription.getText().trim()); - ListModel model = getActionsList().getModel(); + ListModel model = myActionsList.getModel(); int size = model.getSize(); String[] ids; if (size == 0) { @@ -124,7 +125,8 @@ class QuickListPanel { return; } - myDisplayName.setText(this.item.getName()); + myName.setText(item.getName()); + myName.setEnabled(QuickListsManager.getInstance().getSchemeManager().isMetadataEditable(item)); myDescription.setText(item.getDescription()); actionsModel.removeAll(); @@ -140,18 +142,6 @@ class QuickListPanel { } } - public JList getActionsList() { - return myActionsList; - } - - public String getDescription() { - return myDescription.getText(); - } - - public String getDisplayName() { - return myDisplayName.getText(); - } - private void includeActionId(@NotNull String id) { if (QuickList.SEPARATOR_ID.equals(id) || actionsModel.getElementIndex(id) == -1) { actionsModel.add(id); diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListsPanel.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListsPanel.java index 28bf992eac38..18f2e8464277 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListsPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListsPanel.java @@ -30,7 +30,7 @@ public class QuickListsPanel extends ConfigurableBase getSettings() { - return QuickListsManager.getInstance().getQuickLists(); + return QuickListsManager.getInstance().getSchemeManager().getAllSchemes(); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListsUi.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListsUi.java index fedda315b451..62cb2d072fb3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListsUi.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListsUi.java @@ -62,7 +62,13 @@ class QuickListsUi implements ConfigurableUi> { public String getName(@NotNull QuickList item) { return item.getName(); } + + @Override + public boolean isRemovable(@NotNull QuickList item) { + return QuickListsManager.getInstance().getSchemeManager().isMetadataEditable(item); + } }; + private final ListModelEditor editor = new ListModelEditor(itemEditor); private JComponent component; @@ -74,6 +80,8 @@ class QuickListsUi implements ConfigurableUi> { final CardLayout cardLayout = new CardLayout(); + // doesn't make any sense (and in any case scheme manager cannot preserve order) + editor.disableUpDownActions(); editor.getList().addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { @@ -90,12 +98,12 @@ class QuickListsUi implements ConfigurableUi> { }); itemPanel = new QuickListPanel(editor.getModel()); - itemPanel.myDisplayName.getDocument().addDocumentListener(new DocumentAdapter() { + itemPanel.myName.getDocument().addDocumentListener(new DocumentAdapter() { @Override protected void textChanged(DocumentEvent e) { QuickList item = itemPanel.item; if (item != null) { - String name = itemPanel.myDisplayName.getText(); + String name = itemPanel.myName.getText(); boolean changed = !item.getName().equals(name); item.setName(name); if (changed) { diff --git a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java index 2487b4773da3..4f545450929d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java @@ -62,15 +62,12 @@ public final class SchemesManagerFactoryImpl extends SchemesManagerFactory imple @Override public void save() { - List errors = null; - for (SchemesManager registeredManager : myRegisteredManagers) { + List errors = new SmartList(); + for (SchemesManagerImpl registeredManager : myRegisteredManagers) { try { - registeredManager.save(); + registeredManager.save(errors); } catch (Throwable e) { - if (errors == null) { - errors = new SmartList(); - } errors.add(e); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.java deleted file mode 100644 index 953530d998fb..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.java +++ /dev/null @@ -1,949 +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.options; - -import com.intellij.openapi.application.AccessToken; -import com.intellij.openapi.application.Application; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.WriteAction; -import com.intellij.openapi.application.ex.DecodeDefaultsUtil; -import com.intellij.openapi.components.RoamingType; -import com.intellij.openapi.components.ServiceManager; -import com.intellij.openapi.components.impl.stores.DirectoryBasedStorage; -import com.intellij.openapi.components.impl.stores.DirectoryStorageData; -import com.intellij.openapi.components.impl.stores.StorageUtil; -import com.intellij.openapi.components.impl.stores.StreamProvider; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.extensions.AbstractExtensionPointBean; -import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.JDOMUtil; -import com.intellij.openapi.util.WriteExternalException; -import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.text.StringUtilRt; -import com.intellij.openapi.vfs.*; -import com.intellij.openapi.vfs.tracker.VirtualFileTracker; -import com.intellij.util.PathUtil; -import com.intellij.util.PathUtilRt; -import com.intellij.util.SmartList; -import com.intellij.util.ThrowableConvertor; -import com.intellij.util.io.URLUtil; -import com.intellij.util.lang.CompoundRuntimeException; -import com.intellij.util.text.UniqueNameGenerator; -import gnu.trove.*; -import org.jdom.Document; -import org.jdom.Element; -import org.jdom.Parent; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URL; -import java.util.*; - -public final class SchemesManagerImpl extends SchemesManager implements SafeWriteRequestor { - private static final Logger LOG = Logger.getInstance(SchemesManagerFactoryImpl.class); - - private final ArrayList mySchemes = new ArrayList(); - private volatile T myCurrentScheme; - @Nullable - private String myCurrentSchemeName; - - private final String myFileSpec; - private final SchemeProcessor myProcessor; - private final RoamingType myRoamingType; - - private final StreamProvider provider; - private final File myIoDir; - private VirtualFile myDir; - - private final String mySchemeExtension; - private final boolean myUpdateExtension; - - private final Set filesToDelete = new THashSet(); - - // scheme could be changed - so, hashcode will be changed - we must use identity hashing strategy - @SuppressWarnings("unchecked") - private final THashMap schemeToInfo = new THashMap(TObjectHashingStrategy.IDENTITY); - - public SchemesManagerImpl(@NotNull String fileSpec, - @NotNull SchemeProcessor processor, - @NotNull RoamingType roamingType, - @Nullable StreamProvider provider, - @NotNull File baseDir) { - myFileSpec = fileSpec; - myProcessor = processor; - myRoamingType = roamingType; - this.provider = provider; - myIoDir = baseDir; - if (processor instanceof SchemeExtensionProvider) { - mySchemeExtension = ((SchemeExtensionProvider)processor).getSchemeExtension(); - myUpdateExtension = ((SchemeExtensionProvider)processor).isUpgradeNeeded(); - } - else { - mySchemeExtension = DirectoryStorageData.DEFAULT_EXT; - myUpdateExtension = false; - } - - Application application = ApplicationManager.getApplication(); - VirtualFileTracker virtualFileTracker = application == null ? null : ServiceManager.getService(VirtualFileTracker.class); - if (virtualFileTracker != null) { - final String baseDirPath = myIoDir.getAbsolutePath().replace(File.separatorChar, '/'); - virtualFileTracker.addTracker(LocalFileSystem.PROTOCOL_PREFIX + baseDirPath, new VirtualFileAdapter() { - @Override - public void contentsChanged(@NotNull VirtualFileEvent event) { - if (event.getRequestor() != null || !isMy(event)) { - return; - } - - E scheme = findExternalizableSchemeByFileName(event.getFile().getName()); - T oldCurrentScheme = null; - if (scheme != null) { - oldCurrentScheme = getCurrentScheme(); - //noinspection unchecked - removeScheme((T)scheme); - myProcessor.onSchemeDeleted(scheme); - } - - E readScheme = readSchemeFromFile(event.getFile(), null); - if (readScheme != null) { - myProcessor.initScheme(readScheme); - myProcessor.onSchemeAdded(readScheme); - - T newCurrentScheme = getCurrentScheme(); - if (oldCurrentScheme != null && newCurrentScheme == null) { - setCurrentSchemeName(readScheme.getName()); - newCurrentScheme = getCurrentScheme(); - } - - if (oldCurrentScheme != newCurrentScheme) { - myProcessor.onCurrentSchemeChanged(oldCurrentScheme); - } - } - } - - @Override - public void fileCreated(@NotNull VirtualFileEvent event) { - if (event.getRequestor() == null) { - if (event.getFile().isDirectory()) { - VirtualFile dir = getDirectory(); - if (event.getFile().equals(dir)) { - for (VirtualFile file : dir.getChildren()) { - if (isMy(file)) { - schemeCreatedExternally(file); - } - } - } - } - else if (isMy(event)) { - schemeCreatedExternally(event.getFile()); - } - } - } - - private void schemeCreatedExternally(@NotNull VirtualFile file) { - E readScheme = readSchemeFromFile(file, null); - if (readScheme != null) { - myProcessor.initScheme(readScheme); - myProcessor.onSchemeAdded(readScheme); - } - } - - @Override - public void fileDeleted(@NotNull VirtualFileEvent event) { - if (event.getRequestor() == null) { - if (event.getFile().isDirectory()) { - VirtualFile dir = myDir; - if (event.getFile().equals(dir)) { - myDir = null; - for (VirtualFile file : dir.getChildren()) { - if (isMy(file)) { - schemeDeletedExternally(file); - } - } - } - } - else if (isMy(event)) { - schemeDeletedExternally(event.getFile()); - } - } - } - - private void schemeDeletedExternally(@NotNull VirtualFile file) { - E scheme = findExternalizableSchemeByFileName(file.getName()); - T oldCurrentScheme = null; - if (scheme != null) { - oldCurrentScheme = getCurrentScheme(); - //noinspection unchecked - removeScheme((T)scheme); - myProcessor.onSchemeDeleted(scheme); - } - - T newCurrentScheme = getCurrentScheme(); - if (oldCurrentScheme != null && newCurrentScheme == null) { - if (!mySchemes.isEmpty()) { - setCurrentSchemeName(mySchemes.get(0).getName()); - newCurrentScheme = getCurrentScheme(); - } - } - - if (oldCurrentScheme != newCurrentScheme) { - myProcessor.onCurrentSchemeChanged(oldCurrentScheme); - } - } - }, false, application); - } - } - - @Override - public void loadBundledScheme(@NotNull String resourceName, @NotNull Object requestor, @NotNull ThrowableConvertor convertor) { - try { - URL url = requestor instanceof AbstractExtensionPointBean - ? (((AbstractExtensionPointBean)requestor).getLoaderForClass().getResource(resourceName)) - : DecodeDefaultsUtil.getDefaults(requestor, resourceName); - if (url == null) { - // Error shouldn't occur during this operation thus we report error instead of info - LOG.error("Cannot read scheme from " + resourceName); - return; - } - Element element = JDOMUtil.load(URLUtil.openStream(url)); - T scheme = convertor.convert(element); - - if (scheme instanceof ExternalizableScheme) { - String fileName = PathUtilRt.getFileName(url.getPath()); - String extension = getFileExtension(fileName, true); - ExternalInfo info = new ExternalInfo(fileName.substring(0, fileName.length() - extension.length()), extension); - info.hash = JDOMUtil.getTreeHash(element, true); - info.schemeName = scheme.getName(); - ExternalInfo oldInfo = schemeToInfo.put((ExternalizableScheme)scheme, info); - LOG.assertTrue(oldInfo == null); - } - - addNewScheme(scheme, false); - } - catch (Throwable e) { - LOG.error("Cannot read scheme from " + resourceName, e); - } - } - - @NotNull - private String getFileExtension(@NotNull CharSequence fileName, boolean allowAny) { - String extension; - if (StringUtilRt.endsWithIgnoreCase(fileName, mySchemeExtension)) { - extension = mySchemeExtension; - } - else if (StringUtilRt.endsWithIgnoreCase(fileName, DirectoryStorageData.DEFAULT_EXT)) { - extension = DirectoryStorageData.DEFAULT_EXT; - } - else if (allowAny) { - extension = PathUtil.getFileExtension(fileName.toString()); - LOG.assertTrue(extension != null); - } - else { - throw new IllegalStateException("Scheme file extension " + fileName + " is unknown, must be filtered out"); - } - return extension; - } - - private boolean isMy(@NotNull VirtualFileEvent event) { - return isMy(event.getFile()); - } - - private boolean isMy(@NotNull VirtualFile file) { - return StringUtilRt.endsWithIgnoreCase(file.getNameSequence(), mySchemeExtension); - } - - @Override - @NotNull - public Collection loadSchemes() { - final Map result = new LinkedHashMap(); - if (provider != null && provider.isEnabled()) { - provider.processChildren(myFileSpec, myRoamingType, new Condition() { - @Override - public boolean value(@NotNull String name) { - return canRead(name); - } - }, new StreamProvider.ChildrenProcessor() { - @Override - public boolean process(@NotNull String name, @NotNull InputStream input) { - loadScheme(name, input, result); - return true; - } - }); - } - else { - VirtualFile dir = getDirectory(); - VirtualFile[] files = dir == null ? null : dir.getChildren(); - if (files != null) { - for (VirtualFile file : files) { - readSchemeFromFile(file, result); - } - } - } - - Collection list = result.values(); - for (E scheme : list) { - myProcessor.initScheme(scheme); - } - return list; - } - - public void reload() { - // we must not remove non-persistent (e.g. predefined) schemes, because we cannot load it (obviously) - for (int i = mySchemes.size() - 1; i >= 0; i--) { - T scheme = mySchemes.get(i); - //noinspection unchecked - if (scheme instanceof ExternalizableScheme && getState(((E)scheme)) != BaseSchemeProcessor.State.NON_PERSISTENT) { - mySchemes.remove(i); - if (scheme == myCurrentScheme) { - myCurrentScheme = null; - } - } - } - - retainExternalInfo(mySchemes); - - loadSchemes(); - } - - @Nullable - private E findExternalizableSchemeByFileName(@NotNull String fileName) { - for (T scheme : mySchemes) { - if (scheme instanceof ExternalizableScheme) { - if (fileName.equals((getFileName((ExternalizableScheme)scheme)) + mySchemeExtension)) { - //noinspection CastConflictsWithInstanceof,unchecked - return (E)scheme; - } - } - } - return null; - } - - private boolean isOverwriteOnLoad(@NotNull T existingScheme) { - if (existingScheme instanceof ExternalizableScheme) { - ExternalInfo info = schemeToInfo.get(existingScheme); - if (info != null && !mySchemeExtension.equals(info.fileExtension)) { - // scheme from file with old extension, so, we must ignore it - return true; - } - } - return false; - } - - @Nullable - private E loadScheme(@NotNull CharSequence fileName, @NotNull InputStream input, @Nullable Map result) { - boolean duringLoad = result != null; - try { - Element element = JDOMUtil.load(input); - E scheme; - if (myProcessor instanceof BaseSchemeProcessor) { - scheme = ((BaseSchemeProcessor)myProcessor).readScheme(element, duringLoad); - } - else { - //noinspection deprecation - scheme = myProcessor.readScheme(new Document((Element)element.detach())); - } - - if (scheme == null) { - return null; - } - - String extension = getFileExtension(fileName, false); - String fileNameWithoutExtension = fileName.subSequence(0, fileName.length() - extension.length()).toString(); - if (duringLoad) { - if (filesToDelete.contains(fileName.toString())) { - LOG.warn("Scheme file " + fileName + " is not loaded because marked to delete"); - return null; - } - - T existingScheme = findSchemeByName(scheme.getName()); - if (existingScheme != null) { - if (isOverwriteOnLoad(existingScheme)) { - removeScheme(existingScheme); - } - else { - // We don't load scheme with duplicated name - if we generate unique name for it, it will be saved then with new name. - // It is not what all can expect. Such situation in most cases indicates error on previous level, so, we just warn about it. - LOG.warn("Scheme file " + fileName + " is not loaded because defines duplicated name " + scheme.getName()); - return null; - } - } - } - - ExternalInfo info = schemeToInfo.get(scheme); - if (info == null) { - info = new ExternalInfo(fileNameWithoutExtension, extension); - schemeToInfo.put(scheme, info); - } - else { - info.setFileNameWithoutExtension(fileNameWithoutExtension, extension); - } - info.hash = JDOMUtil.getTreeHash(element, true); - info.schemeName = scheme.getName(); - - //noinspection unchecked - T s = (T)scheme; - if (duringLoad) { - mySchemes.add(s); - } - else { - addScheme(s); - } - - if (result != null) { - result.put(scheme.getName(), scheme); - } - return scheme; - } - catch (Exception e) { - LOG.error("Cannot read scheme " + fileName, e); - } - return null; - } - - @Nullable - private String getFileName(@NotNull ExternalizableScheme scheme) { - ExternalInfo info = schemeToInfo.get(scheme); - return info == null ? null : info.fileNameWithoutExtension; - } - - private boolean canRead(@NotNull CharSequence name) { - return myUpdateExtension && StringUtilRt.endsWithIgnoreCase(name, DirectoryStorageData.DEFAULT_EXT) || StringUtilRt.endsWithIgnoreCase(name, mySchemeExtension); - } - - @Nullable - private E readSchemeFromFile(@NotNull VirtualFile file, @Nullable Map result) { - CharSequence fileName = file.getNameSequence(); - if (file.isDirectory() || !canRead(fileName)) { - return null; - } - - try { - return loadScheme(fileName, file.getInputStream(), result); - } - catch (IOException e) { - LOG.error("Cannot read scheme " + fileName, e); - } - return null; - } - - @Override - public void save() { - boolean hasSchemes = false; - UniqueNameGenerator nameGenerator = new UniqueNameGenerator(); - List schemesToSave = new SmartList(); - for (T scheme : mySchemes) { - if (scheme instanceof ExternalizableScheme) { - //noinspection CastConflictsWithInstanceof,unchecked - E eScheme = (E)scheme; - BaseSchemeProcessor.State state = getState(eScheme); - if (state == BaseSchemeProcessor.State.NON_PERSISTENT) { - continue; - } - - hasSchemes = true; - - if (state != BaseSchemeProcessor.State.UNCHANGED) { - schemesToSave.add(eScheme); - } - - String fileName = getFileName(eScheme); - if (fileName != null && !isRenamed(eScheme)) { - nameGenerator.addExistingName(fileName); - } - } - } - - List errors = null; - - for (E scheme : schemesToSave) { - try { - saveScheme(scheme, nameGenerator); - } - catch (Throwable e) { - if (errors == null) { - errors = new SmartList(); - } - errors.add(e); - } - } - - VirtualFile dir = getDirectory(); - errors = deleteFiles(dir, errors); - - if (!hasSchemes && dir != null) { - errors = removeDirectoryIfEmpty(dir, errors); - } - - CompoundRuntimeException.doThrow(errors); - } - - @Nullable - private List removeDirectoryIfEmpty(@NotNull VirtualFile dir, @Nullable List errors) { - for (VirtualFile file : dir.getChildren()) { - if (!file.is(VFileProperty.HIDDEN)) { - LOG.info("Directory " + dir.getNameSequence() + " is not deleted: at least one file " + file.getNameSequence() + " exists"); - return errors; - } - } - - LOG.info("Remove schemes directory " + dir.getNameSequence()); - myDir = null; - - AccessToken token = WriteAction.start(); - try { - dir.delete(this); - } - catch (Throwable e) { - if (errors == null) { - errors = new SmartList(); - } - errors.add(e); - } - finally { - token.finish(); - } - return errors; - } - - @NotNull - private BaseSchemeProcessor.State getState(@NotNull E scheme) { - if (myProcessor instanceof BaseSchemeProcessor) { - return ((BaseSchemeProcessor)myProcessor).getState(scheme); - } - else { - //noinspection deprecation - return myProcessor.shouldBeSaved(scheme) ? BaseSchemeProcessor.State.POSSIBLY_CHANGED : BaseSchemeProcessor.State.NON_PERSISTENT; - } - } - - private void saveScheme(@NotNull E scheme, @NotNull UniqueNameGenerator nameGenerator) throws WriteExternalException, IOException { - @Nullable - ExternalInfo externalInfo = schemeToInfo.get(scheme); - String currentFileNameWithoutExtension = externalInfo == null ? null : externalInfo.fileNameWithoutExtension; - Parent parent = myProcessor.writeScheme(scheme); - Element element = parent == null || parent instanceof Element ? (Element)parent : ((Document)parent).detachRootElement(); - if (JDOMUtil.isEmpty(element)) { - if (externalInfo != null) { - scheduleDelete(externalInfo); - } - return; - } - - String fileNameWithoutExtension = currentFileNameWithoutExtension; - if (fileNameWithoutExtension == null || isRenamed(scheme)) { - fileNameWithoutExtension = nameGenerator.generateUniqueName(FileUtil.sanitizeName(scheme.getName())); - } - - int newHash = JDOMUtil.getTreeHash(element, true); - if (externalInfo != null && currentFileNameWithoutExtension == fileNameWithoutExtension && newHash == externalInfo.hash) { - return; - } - - String fileName = fileNameWithoutExtension + mySchemeExtension; - // file will be overwritten, so, we don't need to delete it - filesToDelete.remove(fileName); - - // stream provider always use LF separator - BufferExposingByteArrayOutputStream byteOut = StorageUtil.writeToBytes(element, "\n"); - - String providerPath; - if (provider != null && provider.isEnabled()) { - providerPath = myFileSpec + '/' + fileName; - if (!provider.isApplicable(providerPath, myRoamingType)) { - providerPath = null; - } - } - else { - providerPath = null; - } - - // if another new scheme uses old name of this scheme, so, we must not delete it (as part of rename operation) - boolean renamed = externalInfo != null && fileNameWithoutExtension != currentFileNameWithoutExtension && nameGenerator.value(currentFileNameWithoutExtension); - if (providerPath == null) { - VirtualFile file = null; - VirtualFile dir = getDirectory(); - if (dir == null || !dir.isValid()) { - dir = DirectoryBasedStorage.createDir(myIoDir, this); - myDir = dir; - } - - if (renamed) { - file = dir.findChild(externalInfo.fileNameWithoutExtension + externalInfo.fileExtension); - if (file != null) { - AccessToken token = WriteAction.start(); - try { - file.rename(this, fileName); - } - finally { - token.finish(); - } - } - } - - if (file == null) { - file = DirectoryBasedStorage.getFile(fileName, dir, this); - } - - AccessToken token = WriteAction.start(); - try { - OutputStream out = file.getOutputStream(this); - try { - byteOut.writeTo(out); - } - finally { - out.close(); - } - } - finally { - token.finish(); - } - } - else { - if (renamed) { - scheduleDelete(externalInfo); - } - provider.saveContent(providerPath, byteOut.getInternalBuffer(), byteOut.size(), myRoamingType); - } - - if (externalInfo == null) { - externalInfo = new ExternalInfo(fileNameWithoutExtension, mySchemeExtension); - schemeToInfo.put(scheme, externalInfo); - } - else { - externalInfo.setFileNameWithoutExtension(fileNameWithoutExtension, mySchemeExtension); - } - externalInfo.hash = newHash; - externalInfo.schemeName = scheme.getName(); - } - - - private void scheduleDelete(@NotNull ExternalInfo externalInfo) { - filesToDelete.add(externalInfo.fileNameWithoutExtension + externalInfo.fileExtension); - } - - private boolean isRenamed(@NotNull ExternalizableScheme scheme) { - ExternalInfo info = schemeToInfo.get(scheme); - return info != null && !scheme.getName().equals(info.schemeName); - } - - @Nullable - private List deleteFiles(@Nullable VirtualFile dir, List errors) { - if (filesToDelete.isEmpty()) { - return errors; - } - - if (provider != null && provider.isEnabled()) { - for (String name : filesToDelete) { - try { - StorageUtil.delete(provider, myFileSpec + '/' + name, myRoamingType); - } - catch (Throwable e) { - if (errors == null) { - errors = new SmartList(); - } - errors.add(e); - } - } - } - else if (dir != null) { - AccessToken token = null; - try { - for (VirtualFile file : dir.getChildren()) { - if (filesToDelete.contains(file.getName())) { - if (token == null) { - token = WriteAction.start(); - } - - try { - file.delete(this); - } - catch (IOException e) { - if (errors == null) { - errors = new SmartList(); - } - errors.add(e); - } - } - } - } - finally { - if (token != null) { - token.finish(); - } - } - } - - filesToDelete.clear(); - return errors; - } - - @Nullable - private VirtualFile getDirectory() { - VirtualFile result = myDir; - if (result == null) { - result = LocalFileSystem.getInstance().findFileByIoFile(myIoDir); - myDir = result; - } - return result; - } - - @Override - public File getRootDirectory() { - return myIoDir; - } - - private void schemeAdded(@NotNull T scheme) { - if (!(scheme instanceof ExternalizableScheme)) { - return; - } - - ExternalInfo info = schemeToInfo.get(scheme); - if (info != null) { - filesToDelete.remove(info.fileNameWithoutExtension + info.fileExtension); - } - } - - @Override - public void setSchemes(@NotNull final List newSchemes, @Nullable Condition removeCondition) { - if (removeCondition == null) { - mySchemes.clear(); - } - else { - for (int i = mySchemes.size() - 1; i >= 0; i--) { - T scheme = mySchemes.get(i); - if (removeCondition.value(scheme)) { - mySchemes.remove(i); - } - } - } - - retainExternalInfo(newSchemes); - - mySchemes.ensureCapacity(newSchemes.size()); - for (T scheme : newSchemes) { - mySchemes.add(scheme); - schemeAdded(scheme); - } - - if (myCurrentSchemeName != null) { - myCurrentScheme = findSchemeByName(myCurrentSchemeName); - if (myCurrentScheme != null) { - return; - } - } - - myCurrentScheme = mySchemes.isEmpty() ? null : mySchemes.get(0); - myCurrentSchemeName = myCurrentScheme == null ? null : myCurrentScheme.getName(); - } - - private void retainExternalInfo(@NotNull final List schemes) { - if (schemeToInfo.isEmpty()) { - return; - } - - schemeToInfo.retainEntries(new TObjectObjectProcedure() { - @Override - public boolean execute(ExternalizableScheme scheme, ExternalInfo info) { - for (T t : schemes) { - if (t == scheme) { - return true; - } - } - - scheduleDelete(info); - return false; - } - }); - } - - @Override - public void addNewScheme(@NotNull T scheme, boolean replaceExisting) { - int toReplace = -1; - for (int i = 0; i < mySchemes.size(); i++) { - T existing = mySchemes.get(i); - if (existing.getName().equals(scheme.getName())) { - if (!Comparing.equal(existing.getClass(), scheme.getClass())) { - LOG.warn("'" + scheme.getName() + "' " + existing.getClass().getSimpleName() + " replaced with " + scheme.getClass().getSimpleName()); - } - - toReplace = i; - if (replaceExisting && existing instanceof ExternalizableScheme) { - ExternalInfo oldInfo = schemeToInfo.remove((ExternalizableScheme)existing); - if (oldInfo != null && scheme instanceof ExternalizableScheme) { - ExternalInfo newInfo = schemeToInfo.get((ExternalizableScheme)scheme); - if (newInfo == null) { - schemeToInfo.put((ExternalizableScheme)scheme, oldInfo); - } - } - } - break; - } - } - if (toReplace == -1) { - mySchemes.add(scheme); - } - else if (replaceExisting || !(scheme instanceof ExternalizableScheme)) { - mySchemes.set(toReplace, scheme); - } - else { - //noinspection unchecked - renameScheme((ExternalizableScheme)scheme, UniqueNameGenerator.generateUniqueName(scheme.getName(), collectExistingNames(mySchemes))); - mySchemes.add(scheme); - } - - schemeAdded(scheme); - } - - @NotNull - private Collection collectExistingNames(@NotNull Collection schemes) { - Set result = new THashSet(schemes.size()); - for (T scheme : schemes) { - result.add(scheme.getName()); - } - return result; - } - - @Override - public void clearAllSchemes() { - schemeToInfo.forEachValue(new TObjectProcedure() { - @Override - public boolean execute(ExternalInfo info) { - scheduleDelete(info); - return true; - } - }); - - myCurrentScheme = null; - mySchemes.clear(); - schemeToInfo.clear(); - } - - @Override - @NotNull - public List getAllSchemes() { - return Collections.unmodifiableList(mySchemes); - } - - @Override - @Nullable - public T findSchemeByName(@NotNull String schemeName) { - for (T scheme : mySchemes) { - if (scheme.getName().equals(schemeName)) { - return scheme; - } - } - return null; - } - - @Override - public void setCurrentSchemeName(@Nullable String schemeName) { - myCurrentSchemeName = schemeName; - myCurrentScheme = null; - } - - @Override - @Nullable - public T getCurrentScheme() { - T scheme = myCurrentScheme; - if (scheme == null && myCurrentSchemeName != null) { - scheme = findSchemeByName(myCurrentSchemeName); - if (scheme != null) { - myCurrentScheme = scheme; - } - } - return scheme; - } - - @Override - public void removeScheme(@NotNull T scheme) { - for (int i = 0, n = mySchemes.size(); i < n; i++) { - T s = mySchemes.get(i); - if (scheme.getName().equals(s.getName())) { - if (myCurrentScheme == s) { - myCurrentScheme = null; - } - - if (s instanceof ExternalizableScheme) { - ExternalInfo info = schemeToInfo.remove((ExternalizableScheme)s); - if (info != null) { - scheduleDelete(info); - } - } - mySchemes.remove(i); - break; - } - } - } - - @Override - @NotNull - public Collection getAllSchemeNames() { - if (mySchemes.isEmpty()) { - return Collections.emptyList(); - } - - List names = new ArrayList(mySchemes.size()); - for (T scheme : mySchemes) { - names.add(scheme.getName()); - } - return names; - } - - private static void renameScheme(@NotNull ExternalizableScheme scheme, @NotNull String newName) { - if (!newName.equals(scheme.getName())) { - scheme.setName(newName); - LOG.assertTrue(newName.equals(scheme.getName())); - } - } - - private static final class ExternalInfo { - public ExternalInfo(@NotNull String fileNameWithoutExtension, @NotNull String fileExtension) { - this.fileNameWithoutExtension = fileNameWithoutExtension; - this.fileExtension = fileExtension; - } - - // we keep it to detect rename - private String schemeName; - - - @NotNull - private String fileNameWithoutExtension; - - private int hash; - - private String fileExtension; - - public void setFileNameWithoutExtension(@NotNull String nameWithoutExtension, @NotNull String extension) { - fileNameWithoutExtension = nameWithoutExtension; - fileExtension = extension; - } - - @Override - public String toString() { - return fileNameWithoutExtension; - } - } - - @Override - public String toString() { - return myFileSpec; - } -} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.kt b/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.kt new file mode 100644 index 000000000000..033003cd446a --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.kt @@ -0,0 +1,830 @@ +/* + * 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.options + +import com.intellij.openapi.application.AccessToken +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.application.WriteAction +import com.intellij.openapi.application.ex.DecodeDefaultsUtil +import com.intellij.openapi.application.writeAction +import com.intellij.openapi.components.RoamingType +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.components.impl.stores.DirectoryBasedStorage +import com.intellij.openapi.components.impl.stores.DirectoryStorageData +import com.intellij.openapi.components.impl.stores.StorageUtil +import com.intellij.openapi.components.impl.stores.StreamProvider +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.extensions.AbstractExtensionPointBean +import com.intellij.openapi.util.Comparing +import com.intellij.openapi.util.Condition +import com.intellij.openapi.util.JDOMUtil +import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.util.text.StringUtilRt +import com.intellij.openapi.vfs.* +import com.intellij.openapi.vfs.tracker.VirtualFileTracker +import com.intellij.util.PathUtil +import com.intellij.util.PathUtilRt +import com.intellij.util.SmartList +import com.intellij.util.ThrowableConvertor +import com.intellij.util.containers.ContainerUtil +import com.intellij.util.io.URLUtil +import com.intellij.util.text.UniqueNameGenerator +import gnu.trove.THashMap +import gnu.trove.THashSet +import gnu.trove.TObjectObjectProcedure +import gnu.trove.TObjectProcedure +import org.jdom.Document +import org.jdom.Element +import java.io.File +import java.io.InputStream +import java.util.ArrayList +import java.util.Collections + +val LOG = Logger.getInstance(javaClass()) + +public class SchemesManagerImpl(private val fileSpec: String, + private val processor: SchemeProcessor, + private val roamingType: RoamingType, + private val provider: StreamProvider?, + private val ioDirectory: File) : SchemesManager(), SafeWriteRequestor { + private val schemes = ArrayList() + private val bundledExternalizableSchemes = THashMap() + + private var currentScheme: T? = null + private var currentSchemeName: String? = null + + private var directory: VirtualFile? = null + + private val schemeExtension: String + private val updateExtension: Boolean + + private val filesToDelete = THashSet() + + // scheme could be changed - so, hashcode will be changed - we must use identity hashing strategy + private val schemeToInfo = THashMap(ContainerUtil.identityStrategy()) + + init { + if (processor is SchemeExtensionProvider) { + schemeExtension = processor.getSchemeExtension() + updateExtension = processor.isUpgradeNeeded() + } + else { + schemeExtension = DirectoryStorageData.DEFAULT_EXT + updateExtension = false + } + + val application = ApplicationManager.getApplication() + val virtualFileTracker = ServiceManager.getService(javaClass()) + if (virtualFileTracker != null) { + val baseDirPath = ioDirectory.getAbsolutePath().replace(File.separatorChar, '/') + virtualFileTracker.addTracker(LocalFileSystem.PROTOCOL_PREFIX + baseDirPath, object : VirtualFileAdapter() { + override fun contentsChanged(event: VirtualFileEvent) { + if (event.getRequestor() != null || !isMy(event)) { + return + } + + val scheme = findExternalizableSchemeByFileName(event.getFile().getName()) + var oldCurrentScheme: T? = null + if (scheme != null) { + oldCurrentScheme = getCurrentScheme() + @suppress("UNCHECKED_CAST") + removeScheme(scheme as T) + processor.onSchemeDeleted(scheme) + } + + val readScheme = readSchemeFromFile(event.getFile(), false) + if (readScheme != null) { + processor.initScheme(readScheme) + processor.onSchemeAdded(readScheme) + + var newCurrentScheme = getCurrentScheme() + if (oldCurrentScheme != null && newCurrentScheme == null) { + setCurrentSchemeName(readScheme.getName()) + newCurrentScheme = getCurrentScheme() + } + + if (oldCurrentScheme !== newCurrentScheme) { + processor.onCurrentSchemeChanged(oldCurrentScheme) + } + } + } + + override fun fileCreated(event: VirtualFileEvent) { + if (event.getRequestor() == null) { + if (event.getFile().isDirectory()) { + val dir = getDirectory() + if (event.getFile() == dir) { + for (file in dir!!.getChildren()) { + if (isMy(file)) { + schemeCreatedExternally(file) + } + } + } + } + else if (isMy(event)) { + schemeCreatedExternally(event.getFile()) + } + } + } + + private fun schemeCreatedExternally(file: VirtualFile) { + val readScheme = readSchemeFromFile(file, false) + if (readScheme != null) { + processor.initScheme(readScheme) + processor.onSchemeAdded(readScheme) + } + } + + override fun fileDeleted(event: VirtualFileEvent) { + if (event.getRequestor() == null) { + if (event.getFile().isDirectory()) { + val dir = directory + if (event.getFile() == dir) { + directory = null + for (file in dir!!.getChildren()) { + if (isMy(file)) { + schemeDeletedExternally(file) + } + } + } + } + else if (isMy(event)) { + schemeDeletedExternally(event.getFile()) + } + } + } + + private fun schemeDeletedExternally(file: VirtualFile) { + val scheme = findExternalizableSchemeByFileName(file.getName()) + var oldCurrentScheme: T? = null + if (scheme != null) { + oldCurrentScheme = getCurrentScheme() + @suppress("UNCHECKED_CAST") + removeScheme(scheme as T) + processor.onSchemeDeleted(scheme) + } + + var newCurrentScheme = getCurrentScheme() + if (oldCurrentScheme != null && newCurrentScheme == null) { + if (!schemes.isEmpty()) { + setCurrentSchemeName(schemes.get(0).getName()) + newCurrentScheme = getCurrentScheme() + } + } + + if (oldCurrentScheme !== newCurrentScheme) { + processor.onCurrentSchemeChanged(oldCurrentScheme) + } + } + }, false, application) + } + } + + override fun loadBundledScheme(resourceName: String, requestor: Any, convertor: ThrowableConvertor) { + try { + val url = if (requestor is AbstractExtensionPointBean) + requestor.getLoaderForClass().getResource(resourceName) + else + DecodeDefaultsUtil.getDefaults(requestor, resourceName) + if (url == null) { + LOG.error("Cannot read scheme from $resourceName") + return + } + + val element = JDOMUtil.load(URLUtil.openStream(url)) + val scheme = convertor.convert(element) + if (scheme is ExternalizableScheme) { + val fileName = PathUtilRt.getFileName(url.getPath()) + val extension = getFileExtension(fileName, true) + val info = ExternalInfo(fileName.substring(0, fileName.length() - extension.length()), extension) + info.hash = JDOMUtil.getTreeHash(element, true) + info.schemeName = scheme.getName() + @suppress("UNCHECKED_CAST") + val oldInfo = schemeToInfo.put(scheme as E, info) + LOG.assertTrue(oldInfo == null) + val oldScheme = bundledExternalizableSchemes.put(scheme.getName(), scheme) + if (oldScheme != null) { + LOG.warn("Duplicated scheme ${scheme.getName()} - old: $oldScheme, new $scheme") + } + } + + schemes.add(scheme) + } + catch (e: Throwable) { + LOG.error("Cannot read scheme from $resourceName", e) + } + } + + private fun getFileExtension(fileName: CharSequence, allowAny: Boolean): String { + return if (StringUtilRt.endsWithIgnoreCase(fileName, schemeExtension)) { + schemeExtension + } + else if (StringUtilRt.endsWithIgnoreCase(fileName, DirectoryStorageData.DEFAULT_EXT)) { + DirectoryStorageData.DEFAULT_EXT + } + else if (allowAny) { + PathUtil.getFileExtension(fileName.toString())!! + } + else { + throw IllegalStateException("Scheme file extension $fileName is unknown, must be filtered out") + } + } + + private fun isMy(event: VirtualFileEvent) = isMy(event.getFile()) + + private fun isMy(file: VirtualFile) = StringUtilRt.endsWithIgnoreCase(file.getNameSequence(), schemeExtension) + + override fun loadSchemes(): Collection { + val newSchemesOffset = schemes.size() + if (provider != null && provider.isEnabled()) { + provider.processChildren(fileSpec, roamingType, { canRead(it) }) { name, input -> + loadScheme(name, input, true) + true + } + } + else { + val dir = getDirectory() + val files = dir?.getChildren() + if (files != null) { + for (file in files) { + readSchemeFromFile(file, true) + } + } + } + + val list = SmartList() + for (i in newSchemesOffset..schemes.size() - 1) { + @suppress("UNCHECKED_CAST") + val scheme = schemes[i] as E + processor.initScheme(scheme) + list.add(scheme) + } + return list + } + + public fun reload() { + // we must not remove non-persistent (e.g. predefined) schemes, because we cannot load it (obviously) + for (i in schemes.indices.reversed()) { + val scheme = schemes.get(i) + @suppress("UNCHECKED_CAST") + if (scheme is ExternalizableScheme && getState(scheme as E) != BaseSchemeProcessor.State.NON_PERSISTENT) { + schemes.remove(i) + if (scheme === currentScheme) { + currentScheme = null + } + } + } + + retainExternalInfo(schemes) + + loadSchemes() + } + + private fun findExternalizableSchemeByFileName(fileName: String): E? { + for (scheme in schemes) { + @suppress("UNCHECKED_CAST") + if (scheme is ExternalizableScheme && fileName == "${getFileName(scheme)}$schemeExtension") { + return scheme as E + } + } + return null + } + + private fun isOverwriteOnLoad(existingScheme: E): Boolean { + if (bundledExternalizableSchemes.get(existingScheme.getName()) === existingScheme) { + // so, bundled scheme is shadowed + return true + } + + val info = schemeToInfo.get(existingScheme) + // scheme from file with old extension, so, we must ignore it + return info != null && schemeExtension != info.fileExtension + } + + private fun loadScheme(fileName: CharSequence, input: InputStream, duringLoad: Boolean): E? { + try { + val element = JDOMUtil.load(input) + @suppress("DEPRECATED_SYMBOL_WITH_MESSAGE", "UNCHECKED_CAST") + val scheme = (if (processor is BaseSchemeProcessor<*>) { + processor.readScheme(element, duringLoad) as E? + } + else { + processor.readScheme(Document(element.detach() as Element)) + }) ?: return null + + val extension = getFileExtension(fileName, false) + val fileNameWithoutExtension = fileName.subSequence(0, fileName.length() - extension.length()).toString() + if (duringLoad) { + if (filesToDelete.isNotEmpty() && filesToDelete.contains(fileName.toString())) { + LOG.warn("Scheme file $fileName is not loaded because marked to delete") + return null + } + + val existingScheme = findSchemeByName(scheme.getName()) + if (existingScheme != null) { + @suppress("UNCHECKED_CAST") + if (existingScheme is ExternalizableScheme && isOverwriteOnLoad(existingScheme as E)) { + removeScheme(existingScheme) + } + else { + // We don't load scheme with duplicated name - if we generate unique name for it, it will be saved then with new name. + // It is not what all can expect. Such situation in most cases indicates error on previous level, so, we just warn about it. + LOG.warn("Scheme file $fileName is not loaded because defines duplicated name ${scheme.getName()}") + return null + } + } + } + + var info: ExternalInfo? = schemeToInfo.get(scheme) + if (info == null) { + info = ExternalInfo(fileNameWithoutExtension, extension) + schemeToInfo.put(scheme, info) + } + else { + info.setFileNameWithoutExtension(fileNameWithoutExtension, extension) + } + info.hash = JDOMUtil.getTreeHash(element, true) + info.schemeName = scheme.getName() + + @suppress("UNCHECKED_CAST") + if (duringLoad) { + schemes.add(scheme as T) + } + else { + addScheme(scheme as T) + } + return scheme + } + catch (e: Exception) { + LOG.error("Cannot read scheme $fileName", e) + return null + } + } + + private fun getFileName(scheme: ExternalizableScheme) = schemeToInfo.get(scheme)?.fileNameWithoutExtension + + private fun canRead(name: CharSequence): Boolean { + return updateExtension && StringUtilRt.endsWithIgnoreCase(name, DirectoryStorageData.DEFAULT_EXT) || StringUtilRt.endsWithIgnoreCase(name, schemeExtension) + } + + private fun readSchemeFromFile(file: VirtualFile, duringLoad: Boolean): E? { + val fileName = file.getNameSequence() + if (file.isDirectory() || !canRead(fileName)) { + return null + } + + try { + return loadScheme(fileName, file.getInputStream(), duringLoad) + } + catch (e: Throwable) { + LOG.error("Cannot read scheme $fileName", e) + return null + } + } + + fun save(errors: MutableList) { + var hasSchemes = false + val nameGenerator = UniqueNameGenerator() + val schemesToSave = SmartList() + for (scheme in schemes) { + @suppress("UNCHECKED_CAST") + if (scheme is ExternalizableScheme) { + val state = getState(scheme as E) + if (state === BaseSchemeProcessor.State.NON_PERSISTENT) { + continue + } + + hasSchemes = true + + if (state !== BaseSchemeProcessor.State.UNCHANGED) { + schemesToSave.add(scheme) + } + + val fileName = getFileName(scheme) + if (fileName != null && !isRenamed(scheme)) { + nameGenerator.addExistingName(fileName) + } + } + } + + for (scheme in schemesToSave) { + errors.catch { + saveScheme(scheme, nameGenerator) + } + } + + val dir = getDirectory() + deleteFiles(dir, errors) + + if (!hasSchemes && dir != null) { + removeDirectoryIfEmpty(dir, errors) + } + } + + private fun removeDirectoryIfEmpty(dir: VirtualFile, errors: MutableList) { + for (file in dir.getChildren()) { + if (!file.`is`(VFileProperty.HIDDEN)) { + LOG.info("Directory " + dir.getNameSequence() + " is not deleted: at least one file " + file.getNameSequence() + " exists") + return + } + } + + LOG.info("Remove schemes directory " + dir.getNameSequence()) + directory = null + + val token = WriteAction.start() + try { + dir.delete(this) + } + catch (e: Throwable) { + errors.add(e) + } + finally { + token.finish() + } + } + + private fun getState(scheme: E): BaseSchemeProcessor.State { + return if (processor is BaseSchemeProcessor<*>) { + (processor as BaseSchemeProcessor).getState(scheme) + } + else { + @suppress("DEPRECATED_SYMBOL_WITH_MESSAGE") + if (processor.shouldBeSaved(scheme)) BaseSchemeProcessor.State.POSSIBLY_CHANGED else BaseSchemeProcessor.State.NON_PERSISTENT + } + } + + private fun saveScheme(scheme: E, nameGenerator: UniqueNameGenerator) { + var externalInfo: ExternalInfo? = schemeToInfo.get(scheme) + val currentFileNameWithoutExtension = if (externalInfo == null) null else externalInfo.fileNameWithoutExtension + val parent = processor.writeScheme(scheme) + val element = if (parent == null || parent is Element) parent as Element? else (parent as Document).detachRootElement() + if (JDOMUtil.isEmpty(element)) { + externalInfo?.scheduleDelete() + return + } + + var fileNameWithoutExtension = currentFileNameWithoutExtension + if (fileNameWithoutExtension == null || isRenamed(scheme)) { + fileNameWithoutExtension = nameGenerator.generateUniqueName(FileUtil.sanitizeName(scheme.getName())) + } + + val newHash = JDOMUtil.getTreeHash(element!!, true) + if (externalInfo != null && currentFileNameWithoutExtension === fileNameWithoutExtension && newHash == externalInfo.hash) { + return + } + + // save only if scheme differs from bundled + val bundledScheme = bundledExternalizableSchemes.get(scheme.getName()) + if (bundledScheme != null && schemeToInfo.get(bundledScheme)!!.hash == newHash) { + externalInfo?.scheduleDelete() + return + } + + val fileName = fileNameWithoutExtension!! + schemeExtension + // file will be overwritten, so, we don't need to delete it + filesToDelete.remove(fileName) + + // stream provider always use LF separator + val byteOut = StorageUtil.writeToBytes(element, "\n") + + var providerPath: String? + if (provider != null && provider.isEnabled()) { + providerPath = fileSpec + '/' + fileName + if (!provider.isApplicable(providerPath, roamingType)) { + providerPath = null + } + } + else { + providerPath = null + } + + // if another new scheme uses old name of this scheme, so, we must not delete it (as part of rename operation) + val renamed = externalInfo != null && fileNameWithoutExtension !== currentFileNameWithoutExtension && nameGenerator.value(currentFileNameWithoutExtension) + if (providerPath == null) { + var file: VirtualFile? = null + var dir = getDirectory() + if (dir == null || !dir.isValid()) { + dir = DirectoryBasedStorage.createDir(ioDirectory, this) + directory = dir!! + } + + if (renamed) { + file = dir.findChild(externalInfo!!.fileName) + if (file != null) { + writeAction { + file!!.rename(this, fileName) + } + } + } + + if (file == null) { + file = DirectoryBasedStorage.getFile(fileName, dir, this) + } + + writeAction { + file!!.getOutputStream(this).use { + byteOut.writeTo(it) + } + } + } + else { + if (renamed) { + externalInfo!!.scheduleDelete() + } + provider!!.saveContent(providerPath, byteOut.getInternalBuffer(), byteOut.size(), roamingType) + } + + if (externalInfo == null) { + externalInfo = ExternalInfo(fileNameWithoutExtension, schemeExtension) + schemeToInfo.put(scheme, externalInfo) + } + else { + externalInfo.setFileNameWithoutExtension(fileNameWithoutExtension, schemeExtension) + } + externalInfo.hash = newHash + externalInfo.schemeName = scheme.getName() + } + + + private fun ExternalInfo.scheduleDelete() { + filesToDelete.add(fileName) + } + + private fun isRenamed(scheme: ExternalizableScheme): Boolean { + val info = schemeToInfo.get(scheme) + return info != null && scheme.getName() != info.schemeName + } + + private fun deleteFiles(dir: VirtualFile?, errors: MutableList) { + if (filesToDelete.isEmpty()) { + return + } + + if (provider != null && provider.isEnabled()) { + for (name in filesToDelete) { + errors.catch { + StorageUtil.delete(provider, fileSpec + '/' + name, roamingType) + } + } + } + else if (dir != null) { + var token: AccessToken? = null + try { + for (file in dir.getChildren()) { + if (filesToDelete.contains(file.getName())) { + if (token == null) { + token = WriteAction.start() + } + + errors.catch { + file.delete(this) + } + } + } + } + finally { + if (token != null) { + token.finish() + } + } + } + + filesToDelete.clear() + } + + private fun getDirectory(): VirtualFile? { + var result = directory + if (result == null) { + result = LocalFileSystem.getInstance().findFileByIoFile(ioDirectory) + directory = result + } + return result + } + + override fun getRootDirectory() = ioDirectory + + override fun setSchemes(newSchemes: List, removeCondition: Condition?) { + if (removeCondition == null) { + schemes.clear() + } + else { + for (i in schemes.indices.reversed()) { + if (removeCondition.value(schemes.get(i))) { + schemes.remove(i) + } + } + } + + retainExternalInfo(newSchemes) + + schemes.addAll(newSchemes) + + if (currentSchemeName != null) { + currentScheme = findSchemeByName(currentSchemeName!!) + if (currentScheme != null) { + return + } + } + + currentScheme = schemes.firstOrNull() + currentSchemeName = currentScheme?.getName() + } + + private fun retainExternalInfo(newSchemes: List) { + if (schemeToInfo.isEmpty()) { + return + } + + schemeToInfo.retainEntries(object : TObjectObjectProcedure { + override fun execute(scheme: E, info: ExternalInfo): Boolean { + if (bundledExternalizableSchemes.get(scheme.getName()) == scheme) { + return true + } + + for (t in newSchemes) { + // by identity + if (t === scheme) { + if (filesToDelete.isNotEmpty()) { + filesToDelete.remove("${info.fileName}") + } + return true + } + } + + info.scheduleDelete() + return false + } + }) + } + + override fun addNewScheme(scheme: T, replaceExisting: Boolean) { + var toReplace = -1 + for (i in schemes.indices) { + val existing = schemes.get(i) + if (existing.getName() == scheme.getName()) { + if (!Comparing.equal>(existing.javaClass, scheme.javaClass)) { + LOG.warn("'${scheme.getName()}' ${existing.javaClass.getSimpleName()} replaced with ${scheme.javaClass.getSimpleName()}") + } + + toReplace = i + if (replaceExisting && existing is ExternalizableScheme) { + val oldInfo = schemeToInfo.remove(existing) + if (oldInfo != null && scheme is ExternalizableScheme && !schemeToInfo.containsKey(scheme)) { + @suppress("UNCHECKED_CAST") + schemeToInfo.put(scheme as E, oldInfo) + } + } + break + } + } + if (toReplace == -1) { + schemes.add(scheme) + } + else if (replaceExisting || scheme !is ExternalizableScheme) { + schemes.set(toReplace, scheme) + } + else { + scheme.renameScheme(UniqueNameGenerator.generateUniqueName(scheme.getName(), collectExistingNames(schemes))) + schemes.add(scheme) + } + + if (scheme is ExternalizableScheme && filesToDelete.isNotEmpty()) { + val info = schemeToInfo.get(scheme) + if (info != null) { + filesToDelete.remove("${info.fileName}") + } + } + } + + private fun collectExistingNames(schemes: Collection): Collection { + val result = THashSet(schemes.size()) + for (scheme in schemes) { + result.add(scheme.getName()) + } + return result + } + + override fun clearAllSchemes() { + schemeToInfo.forEachValue(object : TObjectProcedure { + override fun execute(info: ExternalInfo): Boolean { + info.scheduleDelete() + return true + } + }) + + currentScheme = null + schemes.clear() + schemeToInfo.clear() + } + + override fun getAllSchemes(): List { + return Collections.unmodifiableList(schemes) + } + + override fun findSchemeByName(schemeName: String): T? { + for (scheme in schemes) { + if (scheme.getName() == schemeName) { + return scheme + } + } + return null + } + + override fun setCurrentSchemeName(schemeName: String?) { + currentSchemeName = schemeName + currentScheme = null + } + + override fun getCurrentScheme(): T? { + var scheme = currentScheme + if (scheme == null && currentSchemeName != null) { + scheme = findSchemeByName(currentSchemeName!!) + if (scheme != null) { + currentScheme = scheme + } + } + return scheme + } + + override fun removeScheme(scheme: T) { + for (i in schemes.size() - 1 downTo 0) { + val s = schemes.get(i) + if (scheme.getName() == s.getName()) { + if (currentScheme == s) { + currentScheme = null + } + + if (s is ExternalizableScheme) { + schemeToInfo.remove(s)?.scheduleDelete() + } + schemes.remove(i) + break + } + } + } + + override fun getAllSchemeNames(): Collection { + if (schemes.isEmpty()) { + return emptyList() + } + + val names = ArrayList(schemes.size()) + for (scheme in schemes) { + names.add(scheme.getName()) + } + return names + } + + override fun isMetadataEditable(scheme: E) = !bundledExternalizableSchemes.containsKey(scheme.name) + + private class ExternalInfo(var fileNameWithoutExtension: String, var fileExtension: String?) { + // we keep it to detect rename + var schemeName: String? = null + var hash: Int = 0 + + val fileName: String + get() = "$fileNameWithoutExtension$fileExtension" + + fun setFileNameWithoutExtension(nameWithoutExtension: String, extension: String) { + fileNameWithoutExtension = nameWithoutExtension + fileExtension = extension + } + + override fun toString() = fileName + } + + override fun toString() = fileSpec +} + +private fun ExternalizableScheme.renameScheme(newName: String) { + if (newName != getName()) { + setName(newName) + LOG.assertTrue(newName == getName()) + } +} + +private inline fun MutableList.catch(runnable: () -> Unit) { + try { + runnable() + } + catch (e: Throwable) { + add(e) + } +} + +inline val Scheme.name: String + get() = getName() \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/util/ui/ListModelEditor.java b/platform/platform-impl/src/com/intellij/util/ui/ListModelEditor.java index ba9208c3ff91..a93d61051905 100644 --- a/platform/platform-impl/src/com/intellij/util/ui/ListModelEditor.java +++ b/platform/platform-impl/src/com/intellij/util/ui/ListModelEditor.java @@ -30,7 +30,7 @@ public class ListModelEditor extends ListModelEditorBase { private JBList list = new JBList(model); - public ListModelEditor(@NotNull final ListItemEditor itemEditor) { + public ListModelEditor(@NotNull ListItemEditor itemEditor) { super(itemEditor); list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); @@ -42,7 +42,7 @@ public class ListModelEditor extends ListModelEditorBase { public void run(AnActionButton button) { if (!model.isEmpty()) { T lastItem = model.getElementAt(model.getSize() - 1); - if (itemEditor.isEmpty(lastItem)) { + if (ListModelEditor.this.itemEditor.isEmpty(lastItem)) { ListScrollingUtil.selectItem(list, ContainerUtil.indexOfIdentity(model.getItems(), lastItem)); return; } @@ -61,6 +61,12 @@ public class ListModelEditor extends ListModelEditorBase { }); } + @NotNull + public ListModelEditor disableUpDownActions() { + toolbarDecorator.disableUpDownActions(); + return this; + } + @NotNull public JComponent createComponent() { return toolbarDecorator.createPanel(); diff --git a/platform/platform-impl/src/com/intellij/util/ui/table/TableModelEditor.java b/platform/platform-impl/src/com/intellij/util/ui/table/TableModelEditor.java index 8af243e1d817..486f68838b60 100644 --- a/platform/platform-impl/src/com/intellij/util/ui/table/TableModelEditor.java +++ b/platform/platform-impl/src/com/intellij/util/ui/table/TableModelEditor.java @@ -98,6 +98,7 @@ public class TableModelEditor extends CollectionModelEditor preferredScrollableViewportHeightInRows(int rows) { table.setPreferredScrollableViewportSize(new Dimension(200, table.getRowHeight() * rows)); return this; @@ -151,11 +152,13 @@ public class TableModelEditor extends CollectionModelEditor disableUpDownActions() { toolbarDecorator.disableUpDownActions(); return this; } + @NotNull public TableModelEditor enabled(boolean value) { table.setEnabled(value); return this; diff --git a/platform/platform-tests/platform-tests.iml b/platform/platform-tests/platform-tests.iml index 8f9812d5588c..365fa91320b4 100644 --- a/platform/platform-tests/platform-tests.iml +++ b/platform/platform-tests/platform-tests.iml @@ -25,5 +25,6 @@ + \ No newline at end of file diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ApplicationStoreTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ApplicationStoreTest.java deleted file mode 100644 index 5a47efde5c13..000000000000 --- a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ApplicationStoreTest.java +++ /dev/null @@ -1,288 +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.components.impl; - -import com.intellij.application.options.PathMacrosImpl; -import com.intellij.openapi.Disposable; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.components.*; -import com.intellij.openapi.components.impl.stores.*; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.CharsetToolkit; -import com.intellij.testFramework.LightPlatformTestCase; -import com.intellij.util.messages.MessageBus; -import com.intellij.util.xmlb.XmlSerializerUtil; -import gnu.trove.THashMap; -import org.intellij.lang.annotations.Language; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.Map; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.MatcherAssert.assertThat; - -public class ApplicationStoreTest extends LightPlatformTestCase { - private File testAppConfig; - private MyComponentStore componentStore; - - @Override - public void setUp() throws Exception { - super.setUp(); - - String testAppConfigPath = System.getProperty("test.app.config.path"); - if (testAppConfigPath == null) { - testAppConfig = FileUtil.createTempDirectory("testAppSettings", null); - } - else { - testAppConfig = new File(FileUtil.expandUserHome(testAppConfigPath)); - } - FileUtil.delete(testAppConfig); - - componentStore = new MyComponentStore(testAppConfig.getAbsolutePath()); - } - - @Override - public void tearDown() throws Exception { - try { - Disposer.dispose(componentStore); - componentStore = null; - } - finally { - try { - super.tearDown(); - } - finally { - FileUtil.delete(testAppConfig); - } - } - } - - public void testStreamProviderSaveIfSeveralStoragesConfigured() throws Throwable { - SeveralStoragesConfigured component = new SeveralStoragesConfigured(); - - MyStreamProvider streamProvider = new MyStreamProvider(); - componentStore.getStateStorageManager().setStreamProvider(streamProvider); - - componentStore.initComponent(component, false); - component.foo = "newValue"; - StoreUtil.save(componentStore, null); - - assertThat( - streamProvider.data.get(RoamingType.PER_USER).get(StoragePathMacros.APP_CONFIG + "/proxy.settings.xml"), - equalTo("\n" + - " \n" + - " \n" + - "")); - } - - public void testLoadFromStreamProvider() throws Exception { - SeveralStoragesConfigured component = new SeveralStoragesConfigured(); - - MyStreamProvider streamProvider = new MyStreamProvider(); - THashMap map = new THashMap(); - map.put(StoragePathMacros.APP_CONFIG + "/proxy.settings.xml", "\n" + - " \n" + - " \n" + - ""); - streamProvider.data.put(RoamingType.PER_USER, map); - - componentStore.getStateStorageManager().setStreamProvider(streamProvider); - componentStore.initComponent(component, false); - assertThat(component.foo, equalTo("newValue")); - } - - public void testRemoveDeprecatedStorageOnWrite() throws Exception { - doRemoveDeprecatedStorageOnWrite(new SeveralStoragesConfigured()); - } - - public void testRemoveDeprecatedStorageOnWrite2() throws Exception { - doRemoveDeprecatedStorageOnWrite(new ActualStorageLast()); - } - - private void doRemoveDeprecatedStorageOnWrite(@NotNull Foo component) throws IOException { - File oldFile = saveConfig("other.xml", "" + - " \n" + - " \n" + - ""); - - saveConfig("proxy.settings.xml", "\n" + - " \n" + - " \n" + - ""); - - componentStore.initComponent(component, false); - assertThat(component.foo, equalTo("new")); - - component.foo = "new2"; - StoreUtil.save(componentStore, null); - - assertThat(oldFile.exists(), equalTo(false)); - } - - @NotNull - private File saveConfig(@NotNull String fileName, @Language("XML") String data) throws IOException { - File file = new File(testAppConfig, fileName); - FileUtil.writeToFile(file, data); - return file; - } - - private static class MyStreamProvider extends StreamProvider { - public final Map> data = new THashMap>(); - - @Override - public void saveContent(@NotNull String fileSpec, - @NotNull byte[] content, - int size, - @NotNull RoamingType roamingType) { - getMap(roamingType).put(fileSpec, new String(content, 0, size, CharsetToolkit.UTF8_CHARSET)); - } - - private Map getMap(@NotNull RoamingType roamingType) { - Map map = data.get(roamingType); - if (map == null) { - map = new THashMap(); - data.put(roamingType, map); - } - return map; - } - - @Nullable - @Override - public InputStream loadContent(@NotNull String fileSpec, @NotNull RoamingType roamingType) throws IOException { - String data = getMap(roamingType).get(fileSpec); - return data == null ? null : new ByteArrayInputStream(data.getBytes(CharsetToolkit.UTF8_CHARSET)); - } - - @Override - public void delete(@NotNull String fileSpec, @NotNull RoamingType roamingType) { - Map map = data.get(roamingType); - if (map != null) { - map.remove(fileSpec); - } - } - } - - static class MyComponentStore extends ComponentStoreImpl implements Disposable { - private final StateStorageManager stateStorageManager; - - MyComponentStore(@NotNull final String testAppConfigPath) { - TrackingPathMacroSubstitutor macroSubstitutor = new ApplicationPathMacroManager().createTrackingSubstitutor(); - stateStorageManager = new StateStorageManagerImpl(macroSubstitutor, "application", this, ApplicationManager.getApplication().getPicoContainer()) { - @NotNull - @Override - protected StorageData createStorageData(@NotNull String fileSpec, @NotNull String filePath) { - return new StorageData("application"); - } - - @Nullable - @Override - protected String getOldStorageSpec(@NotNull Object component, @NotNull String componentName, @NotNull StateStorageOperation operation) { - return null; - } - - @Override - protected TrackingPathMacroSubstitutor getMacroSubstitutor(@NotNull final String fileSpec) { - if (fileSpec.equals(StoragePathMacros.APP_CONFIG + "/" + PathMacrosImpl.EXT_FILE_NAME + ".xml")) { - return null; - } - return super.getMacroSubstitutor(fileSpec); - } - }; - - stateStorageManager.addMacro(StoragePathMacros.APP_CONFIG, testAppConfigPath); - } - - @Override - public void load() { - } - - @NotNull - @Override - public StateStorageManager getStateStorageManager() { - return stateStorageManager; - } - - @Override - public void dispose() { - } - - @Nullable - @Override - protected PathMacroManager getPathMacroManagerForDefaults() { - return null; - } - - @NotNull - @Override - protected MessageBus getMessageBus() { - return ApplicationManager.getApplication().getMessageBus(); - } - } - - abstract static class Foo { - public String foo = "defaultValue"; - } - - @State( - name = "HttpConfigurable", - storages = { - @Storage(file = StoragePathMacros.APP_CONFIG + "/proxy.settings.xml"), - @Storage(file = StoragePathMacros.APP_CONFIG + "/other.xml", deprecated = true) - } - ) - static class SeveralStoragesConfigured extends Foo implements PersistentStateComponent { - @Nullable - @Override - public SeveralStoragesConfigured getState() { - return this; - } - - @Override - public void loadState(SeveralStoragesConfigured state) { - XmlSerializerUtil.copyBean(state, this); - } - } - - @State( - name = "HttpConfigurable", - storages = { - @Storage(file = StoragePathMacros.APP_CONFIG + "/other.xml", deprecated = true), - @Storage(file = StoragePathMacros.APP_CONFIG + "/proxy.settings.xml") - } - ) - static class ActualStorageLast extends Foo implements PersistentStateComponent { - @Nullable - @Override - public ActualStorageLast getState() { - return this; - } - - @Override - public void loadState(ActualStorageLast state) { - XmlSerializerUtil.copyBean(state, this); - } - } -} diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ApplicationStoreTest.kt b/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ApplicationStoreTest.kt new file mode 100644 index 000000000000..2178f1fb4698 --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ApplicationStoreTest.kt @@ -0,0 +1,225 @@ +/* + * 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.components.impl + +import com.intellij.application.options.PathMacrosImpl +import com.intellij.openapi.Disposable +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.components.* +import com.intellij.openapi.components.impl.stores.* +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.vfs.CharsetToolkit +import com.intellij.testFramework.LightPlatformTestCase +import com.intellij.util.messages.MessageBus +import com.intellij.util.xmlb.XmlSerializerUtil +import gnu.trove.THashMap +import org.hamcrest.CoreMatchers.equalTo +import org.hamcrest.MatcherAssert.assertThat +import org.intellij.lang.annotations.Language +import java.io.ByteArrayInputStream +import java.io.File +import java.io.IOException +import java.io.InputStream + +public class ApplicationStoreTest : LightPlatformTestCase() { + private var testAppConfig: File? = null + private var componentStore: MyComponentStore? = null + + throws(Exception::class) + override fun setUp() { + super.setUp() + + val testAppConfigPath = System.getProperty("test.app.config.path") + if (testAppConfigPath == null) { + testAppConfig = FileUtil.createTempDirectory("testAppSettings", null) + } + else { + testAppConfig = File(FileUtil.expandUserHome(testAppConfigPath)) + } + FileUtil.delete(testAppConfig!!) + + componentStore = MyComponentStore(testAppConfig!!.getAbsolutePath()) + } + + throws(Exception::class) + override fun tearDown() { + try { + Disposer.dispose(componentStore!!) + componentStore = null + } + finally { + try { + super.tearDown() + } + finally { + FileUtil.delete(testAppConfig!!) + } + } + } + + public fun testStreamProviderSaveIfSeveralStoragesConfigured() { + val component = SeveralStoragesConfigured() + + val streamProvider = MyStreamProvider() + componentStore!!.getStateStorageManager().setStreamProvider(streamProvider) + + componentStore!!.initComponent(component, false) + component.foo = "newValue" + StoreUtil.save(componentStore!!, null) + + assertThat(streamProvider.data.get(RoamingType.PER_USER)!!.get(StoragePathMacros.APP_CONFIG + "/proxy.settings.xml"), equalTo("\n" + " \n" + " \n" + "")) + } + + public fun testLoadFromStreamProvider() { + val component = SeveralStoragesConfigured() + + val streamProvider = MyStreamProvider() + val map = THashMap() + map.put(StoragePathMacros.APP_CONFIG + "/proxy.settings.xml", "\n" + " \n" + " \n" + "") + streamProvider.data.put(RoamingType.PER_USER, map) + + componentStore!!.getStateStorageManager().setStreamProvider(streamProvider) + componentStore!!.initComponent(component, false) + assertThat(component.foo, equalTo("newValue")) + } + + public fun testRemoveDeprecatedStorageOnWrite() { + } + + public fun testRemoveDeprecatedStorageOnWrite2() { + doRemoveDeprecatedStorageOnWrite(ActualStorageLast()) + } + + private fun doRemoveDeprecatedStorageOnWrite(component: Foo) { + val oldFile = saveConfig("other.xml", "" + " \n" + " \n" + "") + + saveConfig("proxy.settings.xml", "\n" + " \n" + " \n" + "") + + componentStore!!.initComponent(component, false) + assertThat(component.foo, equalTo("new")) + + component.foo = "new2" + StoreUtil.save(componentStore!!, null) + + assertThat(oldFile.exists(), equalTo(false)) + } + + throws(IOException::class) + private fun saveConfig(fileName: String, Language("XML") data: String): File { + val file = File(testAppConfig, fileName) + FileUtil.writeToFile(file, data) + return file + } + + private class MyStreamProvider : StreamProvider { + public val data: MutableMap> = THashMap() + + override fun saveContent(fileSpec: String, content: ByteArray, size: Int, roamingType: RoamingType) { + getMap(roamingType).put(fileSpec, String(content, 0, size, CharsetToolkit.UTF8_CHARSET)) + } + + private fun getMap(roamingType: RoamingType): MutableMap { + var map = data.get(roamingType) + if (map == null) { + map = THashMap() + data.put(roamingType, map) + } + return map + } + + throws(IOException::class) + override fun loadContent(fileSpec: String, roamingType: RoamingType): InputStream? { + val data = getMap(roamingType).get(fileSpec) + return if (data == null) null else ByteArrayInputStream(data.toByteArray(CharsetToolkit.UTF8_CHARSET)) + } + + override fun delete(fileSpec: String, roamingType: RoamingType) { + val map = data.get(roamingType) + map?.remove(fileSpec) + } + } + + class MyComponentStore(testAppConfigPath: String) : ComponentStoreImpl(), Disposable { + private val stateStorageManager: StateStorageManager + + init { + val macroSubstitutor = ApplicationPathMacroManager().createTrackingSubstitutor() + stateStorageManager = object : StateStorageManagerImpl(macroSubstitutor, "application", this, ApplicationManager.getApplication().getPicoContainer()) { + override fun createStorageData(fileSpec: String, filePath: String): StorageData { + return StorageData("application") + } + + override fun getOldStorageSpec(component: Any, componentName: String, operation: StateStorageOperation): String? { + return null + } + + override fun getMacroSubstitutor(fileSpec: String): TrackingPathMacroSubstitutor? { + if (fileSpec == StoragePathMacros.APP_CONFIG + "/" + PathMacrosImpl.EXT_FILE_NAME + ".xml") { + return null + } + return super.getMacroSubstitutor(fileSpec) + } + } + + stateStorageManager.addMacro(StoragePathMacros.APP_CONFIG, testAppConfigPath) + } + + override fun load() { + } + + override fun getStateStorageManager(): StateStorageManager { + return stateStorageManager + } + + override fun dispose() { + } + + override fun getPathMacroManagerForDefaults(): PathMacroManager? { + return null + } + + override fun getMessageBus(): MessageBus { + return ApplicationManager.getApplication().getMessageBus() + } + } + + abstract class Foo { + public var foo: String = "defaultValue" + } + + State(name = "HttpConfigurable", storages = arrayOf(Storage(file = StoragePathMacros.APP_CONFIG + "/proxy.settings.xml"), Storage(file = StoragePathMacros.APP_CONFIG + "/other.xml", deprecated = true))) + class SeveralStoragesConfigured : Foo(), PersistentStateComponent { + override fun getState(): SeveralStoragesConfigured? { + return this + } + + override fun loadState(state: SeveralStoragesConfigured) { + XmlSerializerUtil.copyBean(state, this) + } + } + + State(name = "HttpConfigurable", storages = arrayOf(Storage(file = StoragePathMacros.APP_CONFIG + "/other.xml", deprecated = true), Storage(file = StoragePathMacros.APP_CONFIG + "/proxy.settings.xml"))) + class ActualStorageLast : Foo(), PersistentStateComponent { + override fun getState(): ActualStorageLast? { + return this + } + + override fun loadState(state: ActualStorageLast) { + XmlSerializerUtil.copyBean(state, this) + } + } +} From 460c86d45365c434c0da52b324c25e11c70ee449 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Wed, 1 Jul 2015 15:01:11 +0200 Subject: [PATCH 073/137] fix name (*sManager -> *Manager) --- .../{SchemesManagerImpl.kt => SchemeManagerImpl.kt} | 2 +- .../openapi/options/SchemesManagerFactoryImpl.java | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) rename platform/platform-impl/src/com/intellij/openapi/options/{SchemesManagerImpl.kt => SchemeManagerImpl.kt} (99%) diff --git a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.kt b/platform/platform-impl/src/com/intellij/openapi/options/SchemeManagerImpl.kt similarity index 99% rename from platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.kt rename to platform/platform-impl/src/com/intellij/openapi/options/SchemeManagerImpl.kt index 033003cd446a..9f476ad7e979 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.kt +++ b/platform/platform-impl/src/com/intellij/openapi/options/SchemeManagerImpl.kt @@ -55,7 +55,7 @@ import java.util.Collections val LOG = Logger.getInstance(javaClass()) -public class SchemesManagerImpl(private val fileSpec: String, +public class SchemeManagerImpl(private val fileSpec: String, private val processor: SchemeProcessor, private val roamingType: RoamingType, private val provider: StreamProvider?, diff --git a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java index 4f545450929d..575948fb3077 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java @@ -34,7 +34,7 @@ import java.util.List; public final class SchemesManagerFactoryImpl extends SchemesManagerFactory implements SettingsSavingComponent { private static final Logger LOG = Logger.getInstance(SchemesManagerFactoryImpl.class); - private final List myRegisteredManagers = ContainerUtil.createLockFreeCopyOnWriteList(); + private final List myRegisteredManagers = ContainerUtil.createLockFreeCopyOnWriteList(); @NotNull @Override @@ -44,13 +44,13 @@ public final class SchemesManagerFactoryImpl extends SchemesManagerFactory imple StateStorageManager storageManager = ((ApplicationImpl)ApplicationManager.getApplication()).getStateStore().getStateStorageManager(); String baseDirPath = storageManager.expandMacros(fileSpec); StreamProvider provider = storageManager.getStreamProvider(); - SchemesManagerImpl manager = new SchemesManagerImpl(fileSpec, processor, roamingType, provider, new File(baseDirPath)); + SchemeManagerImpl manager = new SchemeManagerImpl(fileSpec, processor, roamingType, provider, new File(baseDirPath)); myRegisteredManagers.add(manager); return manager; } - public void process(@NotNull Consumer processor) { - for (SchemesManagerImpl manager : myRegisteredManagers) { + public void process(@NotNull Consumer processor) { + for (SchemeManagerImpl manager : myRegisteredManagers) { try { processor.consume(manager); } @@ -63,7 +63,7 @@ public final class SchemesManagerFactoryImpl extends SchemesManagerFactory imple @Override public void save() { List errors = new SmartList(); - for (SchemesManagerImpl registeredManager : myRegisteredManagers) { + for (SchemeManagerImpl registeredManager : myRegisteredManagers) { try { registeredManager.save(errors); } From f18b59f9cd3a5b916578278a23366188a66fdaea Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Wed, 1 Jul 2015 17:30:17 +0200 Subject: [PATCH 074/137] implement `scheme overrides read-only` Settings Repository tests uses scheme manager test classes (processor, scheme, helper methods), so, we move scheme manager test from idea-tests to platform-tests (as it should be) to avoid dependency on java test framework. --- .../built-in-server/testSrc/TestManager.kt | 2 +- .../intellij/openapi/application/actions.kt | 14 +- .../components/impl/stores/StreamProvider.kt | 4 +- .../openapi/options/SchemeManagerImpl.kt | 23 +- .../options/different-extensions/1.icls | 1 + .../options/different-extensions/1.xml | 1 + .../testData/options/options/1.xml | 1 + .../testData/options/options1/1.xml | 1 + .../testData/options/options1/2.xml | 1 + .../testSrc/bundledSchemes/default.xml | 1 + .../intellij/options/MockStreamProvider.kt | 40 ++ .../com/intellij/options/SchemeManagerTest.kt | 368 ++++++++++++++++++ platform/testFramework/testSrc/FixtureRule.kt | 6 +- 13 files changed, 443 insertions(+), 20 deletions(-) create mode 100644 platform/platform-tests/testData/options/different-extensions/1.icls create mode 100644 platform/platform-tests/testData/options/different-extensions/1.xml create mode 100644 platform/platform-tests/testData/options/options/1.xml create mode 100644 platform/platform-tests/testData/options/options1/1.xml create mode 100644 platform/platform-tests/testData/options/options1/2.xml create mode 100644 platform/platform-tests/testSrc/bundledSchemes/default.xml create mode 100644 platform/platform-tests/testSrc/com/intellij/options/MockStreamProvider.kt create mode 100644 platform/platform-tests/testSrc/com/intellij/options/SchemeManagerTest.kt diff --git a/platform/built-in-server/testSrc/TestManager.kt b/platform/built-in-server/testSrc/TestManager.kt index 05f13c915c89..be3fc2ef57e3 100644 --- a/platform/built-in-server/testSrc/TestManager.kt +++ b/platform/built-in-server/testSrc/TestManager.kt @@ -1,5 +1,6 @@ package org.jetbrains.ide +import com.intellij.openapi.application.invokeAndWaitIfNeed import com.intellij.openapi.application.writeAction import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.roots.ModuleRootModificationUtil @@ -7,7 +8,6 @@ import com.intellij.openapi.util.io.FileUtilRt import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.vfs.VirtualFile import com.intellij.testFramework.fixtures.IdeaProjectTestFixture -import org.jetbrains.testFramework.invokeAndWaitIfNeed import org.junit.rules.TestWatcher import org.junit.runner.Description import java.io.File diff --git a/platform/core-impl/src/com/intellij/openapi/application/actions.kt b/platform/core-impl/src/com/intellij/openapi/application/actions.kt index df6f89b514bc..4be236085b50 100644 --- a/platform/core-impl/src/com/intellij/openapi/application/actions.kt +++ b/platform/core-impl/src/com/intellij/openapi/application/actions.kt @@ -15,6 +15,8 @@ */ package com.intellij.openapi.application +import javax.swing.SwingUtilities + public inline fun writeAction(runnable: () -> Unit) { val token = WriteAction.start() try { @@ -23,4 +25,14 @@ public inline fun writeAction(runnable: () -> Unit) { finally { token.finish() } -} \ No newline at end of file +} + +public fun invokeAndWaitIfNeed(runnable: () -> Unit) { + val app = ApplicationManager.getApplication() + if (app == null) { + if (SwingUtilities.isEventDispatchThread()) runnable() else SwingUtilities.invokeAndWait(runnable) + } + else { + if (app.isDispatchThread()) runnable() else app.invokeAndWait(runnable, ModalityState.any()) + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.kt b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.kt index 86a31f2185bc..df166e62a043 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.kt +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.kt @@ -43,7 +43,7 @@ public interface StreamProvider { /** * You must close passed input stream. */ - public open fun processChildren(path: String, roamingType: RoamingType, filter: (name: String) -> Boolean, processor: (name: String, input: InputStream) -> Boolean) { + public open fun processChildren(path: String, roamingType: RoamingType, filter: (name: String) -> Boolean, processor: (name: String, input: InputStream, readOnly: Boolean) -> Boolean) { for (name in listSubFiles(path, roamingType)) { if (!filter(name)) { continue @@ -59,7 +59,7 @@ public interface StreamProvider { } - if (input != null && !processor(name, input)) { + if (input != null && !processor(name, input, false)) { break } } diff --git a/platform/platform-impl/src/com/intellij/openapi/options/SchemeManagerImpl.kt b/platform/platform-impl/src/com/intellij/openapi/options/SchemeManagerImpl.kt index 9f476ad7e979..f2e4ac8eac7a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/SchemeManagerImpl.kt +++ b/platform/platform-impl/src/com/intellij/openapi/options/SchemeManagerImpl.kt @@ -61,7 +61,7 @@ public class SchemeManagerImpl(private val private val provider: StreamProvider?, private val ioDirectory: File) : SchemesManager(), SafeWriteRequestor { private val schemes = ArrayList() - private val bundledExternalizableSchemes = THashMap() + private val readOnlyExternalizableSchemes = THashMap() private var currentScheme: T? = null private var currentSchemeName: String? = null @@ -215,7 +215,7 @@ public class SchemeManagerImpl(private val @suppress("UNCHECKED_CAST") val oldInfo = schemeToInfo.put(scheme as E, info) LOG.assertTrue(oldInfo == null) - val oldScheme = bundledExternalizableSchemes.put(scheme.getName(), scheme) + val oldScheme = readOnlyExternalizableSchemes.put(scheme.getName(), scheme) if (oldScheme != null) { LOG.warn("Duplicated scheme ${scheme.getName()} - old: $oldScheme, new $scheme") } @@ -250,8 +250,11 @@ public class SchemeManagerImpl(private val override fun loadSchemes(): Collection { val newSchemesOffset = schemes.size() if (provider != null && provider.isEnabled()) { - provider.processChildren(fileSpec, roamingType, { canRead(it) }) { name, input -> - loadScheme(name, input, true) + provider.processChildren(fileSpec, roamingType, { canRead(it) }) { name, input, readOnly -> + val scheme = loadScheme(name, input, true) + if (readOnly && scheme != null) { + readOnlyExternalizableSchemes.put(scheme.name, scheme) + } true } } @@ -304,7 +307,7 @@ public class SchemeManagerImpl(private val } private fun isOverwriteOnLoad(existingScheme: E): Boolean { - if (bundledExternalizableSchemes.get(existingScheme.getName()) === existingScheme) { + if (readOnlyExternalizableSchemes.get(existingScheme.getName()) === existingScheme) { // so, bundled scheme is shadowed return true } @@ -488,7 +491,7 @@ public class SchemeManagerImpl(private val } // save only if scheme differs from bundled - val bundledScheme = bundledExternalizableSchemes.get(scheme.getName()) + val bundledScheme = readOnlyExternalizableSchemes.get(scheme.getName()) if (bundledScheme != null && schemeToInfo.get(bundledScheme)!!.hash == newHash) { externalInfo?.scheduleDelete() return @@ -651,7 +654,7 @@ public class SchemeManagerImpl(private val schemeToInfo.retainEntries(object : TObjectObjectProcedure { override fun execute(scheme: E, info: ExternalInfo): Boolean { - if (bundledExternalizableSchemes.get(scheme.getName()) == scheme) { + if (readOnlyExternalizableSchemes.get(scheme.getName()) == scheme) { return true } @@ -731,9 +734,7 @@ public class SchemeManagerImpl(private val schemeToInfo.clear() } - override fun getAllSchemes(): List { - return Collections.unmodifiableList(schemes) - } + override fun getAllSchemes() = Collections.unmodifiableList(schemes) override fun findSchemeByName(schemeName: String): T? { for (scheme in schemes) { @@ -789,7 +790,7 @@ public class SchemeManagerImpl(private val return names } - override fun isMetadataEditable(scheme: E) = !bundledExternalizableSchemes.containsKey(scheme.name) + override fun isMetadataEditable(scheme: E) = !readOnlyExternalizableSchemes.containsKey(scheme.name) private class ExternalInfo(var fileNameWithoutExtension: String, var fileExtension: String?) { // we keep it to detect rename diff --git a/platform/platform-tests/testData/options/different-extensions/1.icls b/platform/platform-tests/testData/options/different-extensions/1.icls new file mode 100644 index 000000000000..2b2964d3870c --- /dev/null +++ b/platform/platform-tests/testData/options/different-extensions/1.icls @@ -0,0 +1 @@ + diff --git a/platform/platform-tests/testData/options/different-extensions/1.xml b/platform/platform-tests/testData/options/different-extensions/1.xml new file mode 100644 index 000000000000..22a3a0bd0441 --- /dev/null +++ b/platform/platform-tests/testData/options/different-extensions/1.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/platform/platform-tests/testData/options/options/1.xml b/platform/platform-tests/testData/options/options/1.xml new file mode 100644 index 000000000000..e4c594a71fec --- /dev/null +++ b/platform/platform-tests/testData/options/options/1.xml @@ -0,0 +1 @@ + diff --git a/platform/platform-tests/testData/options/options1/1.xml b/platform/platform-tests/testData/options/options1/1.xml new file mode 100644 index 000000000000..c023fb61fd3d --- /dev/null +++ b/platform/platform-tests/testData/options/options1/1.xml @@ -0,0 +1 @@ + diff --git a/platform/platform-tests/testData/options/options1/2.xml b/platform/platform-tests/testData/options/options1/2.xml new file mode 100644 index 000000000000..e5b1786c1ad0 --- /dev/null +++ b/platform/platform-tests/testData/options/options1/2.xml @@ -0,0 +1 @@ + diff --git a/platform/platform-tests/testSrc/bundledSchemes/default.xml b/platform/platform-tests/testSrc/bundledSchemes/default.xml new file mode 100644 index 000000000000..0cb9dad410e4 --- /dev/null +++ b/platform/platform-tests/testSrc/bundledSchemes/default.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/platform/platform-tests/testSrc/com/intellij/options/MockStreamProvider.kt b/platform/platform-tests/testSrc/com/intellij/options/MockStreamProvider.kt new file mode 100644 index 000000000000..e7ed0ff3b495 --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/options/MockStreamProvider.kt @@ -0,0 +1,40 @@ +package com.intellij.options + +import com.intellij.openapi.components.RoamingType +import com.intellij.openapi.components.impl.stores.StreamProvider +import com.intellij.openapi.util.io.FileUtil +import com.intellij.util.SmartList +import java.io.File +import java.io.FileInputStream +import java.io.InputStream + +class MockStreamProvider(private val myBaseDir: File) : StreamProvider { + override fun isApplicable(fileSpec: String, roamingType: RoamingType) = roamingType === RoamingType.PER_USER + + override fun saveContent(fileSpec: String, content: ByteArray, size: Int, roamingType: RoamingType) { + FileUtil.writeToFile(File(myBaseDir, fileSpec), content, 0, size) + } + + override fun loadContent(fileSpec: String, roamingType: RoamingType): InputStream? { + val file = File(myBaseDir, fileSpec) + //noinspection IOResourceOpenedButNotSafelyClosed + return if (file.exists()) FileInputStream(file) else null + } + + override fun listSubFiles(fileSpec: String, roamingType: RoamingType): Collection { + if (roamingType !== RoamingType.PER_USER) { + return emptyList() + } + + val files = File(myBaseDir, fileSpec).listFiles() ?: return emptyList() + val names = SmartList() + for (file in files) { + names.add(file.getName()) + } + return names + } + + override fun delete(fileSpec: String, roamingType: RoamingType) { + FileUtil.delete(File(myBaseDir, fileSpec)) + } +} diff --git a/platform/platform-tests/testSrc/com/intellij/options/SchemeManagerTest.kt b/platform/platform-tests/testSrc/com/intellij/options/SchemeManagerTest.kt new file mode 100644 index 000000000000..2cb9d57572d9 --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/options/SchemeManagerTest.kt @@ -0,0 +1,368 @@ +package com.intellij.options + +import com.intellij.openapi.application.invokeAndWaitIfNeed +import com.intellij.openapi.components.RoamingType +import com.intellij.openapi.options.BaseSchemeProcessor +import com.intellij.openapi.options.ExternalizableScheme +import com.intellij.openapi.options.SchemeManagerImpl +import com.intellij.openapi.util.JDOMUtil +import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream +import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.util.text.StringUtil +import com.intellij.testFramework.PlatformTestUtil +import com.intellij.util.SmartList +import com.intellij.util.lang.CompoundRuntimeException +import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters +import com.intellij.util.xmlb.XmlSerializer +import com.intellij.util.xmlb.annotations.Attribute +import com.intellij.util.xmlb.annotations.Tag +import com.intellij.util.xmlb.annotations.Transient +import gnu.trove.THashMap +import org.hamcrest.CoreMatchers.equalTo +import org.hamcrest.CoreMatchers.not +import org.hamcrest.CoreMatchers.notNullValue +import org.hamcrest.CoreMatchers.sameInstance +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.collection.IsMapContaining.hasKey +import org.jdom.Element +import org.jetbrains.testFramework.FixtureRule +import org.jetbrains.testFramework.TemporaryDirectory +import org.jetbrains.testFramework.exists +import org.junit.Rule +import org.junit.Test +import java.io.File + +val FILE_SPEC = "REMOTE" + +class SchemeManagerTest { + private val fixtureManager = FixtureRule() + public Rule fun getFixtureManager(): FixtureRule = fixtureManager + + private val tempDirManager = TemporaryDirectory() + public Rule fun getTemporaryFolder(): TemporaryDirectory = tempDirManager + + private var localBaseDir: File? = null + private var remoteBaseDir: File? = null + + private fun getTestDataPath() = PlatformTestUtil.getCommunityPath().replace(File.separatorChar, '/') + "/platform/platform-tests/testData/options" + + public Test fun testLoadSchemes() { + doLoadSaveTest("options1", "1->first;2->second") + } + + public Test fun testLoadSimpleSchemes() { + doLoadSaveTest("options", "1->1") + } + + public Test fun testDeleteScheme() { + val manager = createAndLoad("options1") + manager.removeScheme(TestScheme("first")) + manager.save() + + checkSchemes("2->second") + } + + public Test fun testRenameScheme() { + val manager = createAndLoad("options1") + + val scheme = manager.findSchemeByName("first") + assertThat(scheme, notNullValue()) + assert(scheme != null) + scheme!!.setName("renamed") + manager.save() + + checkSchemes("2->second;renamed->renamed") + } + + public Test fun testRenameScheme2() { + val manager = createAndLoad("options1") + + val first = manager.findSchemeByName("first") + assertThat(first, notNullValue()) + assert(first != null) + first!!.setName("2") + val second = manager.findSchemeByName("second") + assertThat(second, notNullValue()) + assert(second != null) + second!!.setName("1") + manager.save() + + checkSchemes("1->1;2->2") + } + + public Test fun testDeleteRenamedScheme() { + val manager = createAndLoad("options1") + + val firstScheme = manager.findSchemeByName("first") + assertThat(firstScheme, notNullValue()) + assert(firstScheme != null) + firstScheme!!.setName("first_renamed") + manager.save() + + checkSchemes(File(remoteBaseDir, "REMOTE"), "first_renamed->first_renamed;2->second", true) + checkSchemes(localBaseDir!!, "", false) + + firstScheme.setName("first_renamed2") + manager.removeScheme(firstScheme) + manager.save() + + checkSchemes(File(remoteBaseDir, "REMOTE"), "2->second", true) + checkSchemes(localBaseDir!!, "", false) + } + + public Test fun testDeleteAndCreateSchemeWithTheSameName() { + val manager = createAndLoad("options1") + val firstScheme = manager.findSchemeByName("first") + assertThat(firstScheme, notNullValue()) + + manager.removeScheme(firstScheme!!) + manager.addScheme(TestScheme("first")) + manager.save() + checkSchemes("2->second;first->first") + } + + public Test fun testGenerateUniqueSchemeName() { + val manager = createAndLoad("options1") + val scheme = TestScheme("first") + manager.addNewScheme(scheme, false) + + assertThat("first2", equalTo(scheme.getName())) + } + + public Test fun `different extensions`() { + val dir = tempDirManager.newDirectory() + FileUtil.copyDir(File("${getTestDataPath()}/different-extensions"), dir, false) + val schemesManager = SchemeManagerImpl(FILE_SPEC, object: TestSchemesProcessor() { + override fun isUpgradeNeeded() = true + + override fun getSchemeExtension() = ".icls" + }, RoamingType.PER_USER, null, dir) + schemesManager.loadSchemes() + val schemes = schemesManager.getAllSchemes() + assertThat(schemes.size(), equalTo(1)) + val scheme = schemes.get(0) + assertThat(scheme.data, equalTo("true")) + + assertThat(File(dir, "1.icls"), exists()) + assertThat(File(dir, "1.xml"), exists()) + + scheme.data = "newTrue" + save(schemesManager) + + assertThat(File(dir, "1.icls"), exists()) + assertThat(File(dir, "1.xml"), not(exists())) + } + + public Test fun setSchemes() { + val dir = tempDirManager.newDirectory() + val schemesManager = createSchemeManager(dir) + schemesManager.loadSchemes() + assertThat(schemesManager.getAllSchemes().isEmpty(), equalTo(true)) + + val scheme = TestScheme("s1") + schemesManager.setSchemes(listOf(scheme)) + + val schemes = schemesManager.getAllSchemes() + assertThat(schemes.size(), equalTo(1)) + assertThat(schemes.get(0), sameInstance(scheme)) + + assertThat(File(dir, "s1.xml"), not(exists())) + + scheme.data = "newTrue" + save(schemesManager) + + assertThat(File(dir, "s1.xml"), exists()) + + schemesManager.setSchemes(emptyList()) + + save(schemesManager) + + assertThat(dir, not(exists())) + } + + public Test fun `save only if scheme differs from bundled`() { + val dir = tempDirManager.newDirectory() + var schemeManager = createSchemeManager(dir) + val converter: (Element) -> TestScheme = { XmlSerializer.deserialize(it, javaClass())!! } + val bundledPath = "/bundledSchemes/default" + schemeManager.loadBundledScheme(bundledPath, this, converter) + var schemes = schemeManager.getAllSchemes() + assertThat(schemes.size(), equalTo(1)) + val customScheme = TestScheme("default") + assertThat(schemes.get(0), equalTo(customScheme)) + + save(schemeManager) + assertThat(dir, not(exists())) + + save(schemeManager) + schemeManager.setSchemes(listOf(customScheme)) + assertThat(dir, not(exists())) + + schemes = schemeManager.getAllSchemes() + assertThat(schemes.size(), equalTo(1)) + assertThat(schemes.get(0), sameInstance(customScheme)) + + customScheme.data = "foo" + save(schemeManager) + val schemeFile = File(dir, "default.xml") + assertThat(schemeFile, exists()) + + schemeManager = createSchemeManager(dir) + schemeManager.loadBundledScheme(bundledPath, this, converter) + schemeManager.loadSchemes() + + schemes = schemeManager.getAllSchemes() + assertThat(schemes.get(0), equalTo(customScheme)) + } + + private fun createSchemeManager(dir: File) = SchemeManagerImpl(FILE_SPEC, TestSchemesProcessor(), RoamingType.PER_USER, null, dir) + + public Test fun `don't remove dir if no schemes but at least one non-hidden file exists`() { + val dir = tempDirManager.newDirectory() + val schemesManager = SchemeManagerImpl(FILE_SPEC, TestSchemesProcessor(), RoamingType.PER_USER, null, dir) + + val scheme = TestScheme("s1") + schemesManager.setSchemes(listOf(scheme)) + + save(schemesManager) + + val schemeFile = File(dir, "s1.xml") + assertThat(schemeFile.exists(), equalTo(true)) + + schemesManager.setSchemes(emptyList()) + + FileUtil.writeToFile(File(dir, "empty"), byteArrayOf()) + + save(schemesManager) + + assertThat(schemeFile.exists(), equalTo(false)) + assertThat(dir.exists(), equalTo(true)) + } + + public Test fun rename() { + val dir = tempDirManager.newDirectory() + val schemesManager = SchemeManagerImpl(FILE_SPEC, TestSchemesProcessor(), RoamingType.PER_USER, null, dir) + schemesManager.loadSchemes() + assertThat(schemesManager.getAllSchemes().isEmpty(), equalTo(true)) + + val scheme = TestScheme("s1") + schemesManager.setSchemes(listOf(scheme)) + + val schemes = schemesManager.getAllSchemes() + assertThat(schemes.size(), equalTo(1)) + assertThat(schemes.get(0), sameInstance(scheme)) + + assertThat(File(dir, "s1.xml").exists(), equalTo(false)) + + scheme.data = "newTrue" + save(schemesManager) + + assertThat(File(dir, "s1.xml").exists(), equalTo(true)) + + scheme.setName("s2") + + save(schemesManager) + + assertThat(File(dir, "s1.xml").exists(), equalTo(false)) + assertThat(File(dir, "s2.xml").exists(), equalTo(true)) + } + + private fun save(schemeManager: SchemeManagerImpl) { + invokeAndWaitIfNeed { + schemeManager.save() + } + } + + private fun createAndLoad(testData: String): SchemeManagerImpl { + createTempFiles(testData) + return createAndLoad() + } + + private fun doLoadSaveTest(testData: String, expected: String, localExpected: String = "") { + val schemesManager = createAndLoad(testData) + schemesManager.save() + checkSchemes(File(remoteBaseDir, "REMOTE"), expected, true) + checkSchemes(localBaseDir!!, localExpected, false) + } + + private fun checkSchemes(expected: String) { + checkSchemes(File(remoteBaseDir, "REMOTE"), expected, true) + checkSchemes(localBaseDir!!, "", false) + } + + private fun createAndLoad(): SchemeManagerImpl { + val schemesManager = SchemeManagerImpl(FILE_SPEC, TestSchemesProcessor(), RoamingType.PER_USER, MockStreamProvider(remoteBaseDir!!), localBaseDir!!) + schemesManager.loadSchemes() + return schemesManager + } + + private fun createTempFiles(testData: String) { + val temp = tempDirManager.newDirectory() + localBaseDir = File(temp, "__local") + remoteBaseDir = temp + FileUtil.copyDir(File("${getTestDataPath()}/$testData"), File(temp, "REMOTE")) + } +} + +private fun checkSchemes(baseDir: File, expected: String, ignoreDeleted: Boolean) { + val filesToScheme = StringUtil.split(expected, ";") + val fileToSchemeMap = THashMap() + for (fileToScheme in filesToScheme) { + val index = fileToScheme.indexOf("->") + fileToSchemeMap.put(fileToScheme.substring(0, index), fileToScheme.substring(index + 2)) + } + + val files = baseDir.listFiles() + if (files != null) { + for (file in files) { + val fileName = FileUtil.getNameWithoutExtension(file) + if ("--deleted" == fileName && ignoreDeleted) { + assertThat>(fileToSchemeMap, hasKey(fileName)) + } + } + } + + for (file in fileToSchemeMap.keySet()) { + assertThat(File(baseDir, "$file.xml").isFile(), equalTo(true)) + } + + if (files != null) { + val schemesProcessor = TestSchemesProcessor() + for (file in files) { + val fileName = FileUtil.getNameWithoutExtension(file) + val scheme = schemesProcessor.readScheme(JDOMUtil.load(file), true) + assertThat(fileToSchemeMap.get(fileName), equalTo(scheme!!.getName())) + } + } +} + +public data Tag("scheme") class TestScheme(Attribute private var name: String = "", Attribute var data: String? = null) : ExternalizableScheme { + override fun getName() = name + + override Transient fun setName(newName: String) { + name = newName + } + + @suppress("DEPRECATED_SYMBOL_WITH_MESSAGE") + override fun getExternalInfo() = null +} + +public open class TestSchemesProcessor : BaseSchemeProcessor() { + override fun readScheme(element: Element) = XmlSerializer.deserialize(element, javaClass()) + + override fun writeScheme(scheme: TestScheme) = scheme.serialize() +} + +fun SchemeManagerImpl<*, *>.save() { + val errors = SmartList() + save(errors) + CompoundRuntimeException.doThrow(errors) +} + +public fun T.serialize(): Element = XmlSerializer.serialize(this, SkipDefaultValuesSerializationFilters()) + +public fun Element.toByteArray(): ByteArray { + val out = BufferExposingByteArrayOutputStream(512) + JDOMUtil.writeParent(this, out, "\n") + return out.toByteArray() +} \ No newline at end of file diff --git a/platform/testFramework/testSrc/FixtureRule.kt b/platform/testFramework/testSrc/FixtureRule.kt index dcca0ef2d24c..06506fa0c744 100644 --- a/platform/testFramework/testSrc/FixtureRule.kt +++ b/platform/testFramework/testSrc/FixtureRule.kt @@ -15,14 +15,10 @@ */ package org.jetbrains.testFramework +import com.intellij.openapi.application.invokeAndWaitIfNeed import com.intellij.testFramework.UsefulTestCase import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory import org.junit.rules.ExternalResource -import javax.swing.SwingUtilities - -public fun invokeAndWaitIfNeed(runnable: () -> Unit) { - if (SwingUtilities.isEventDispatchThread()) runnable() else SwingUtilities.invokeAndWait(runnable) -} public class FixtureRule() : ExternalResource() { val projectFixture = IdeaTestFixtureFactory.getFixtureFactory().createLightFixtureBuilder().getFixture() From a6b1d4a2ec8dfca6e93ca7b1dad5cfce430b5dc9 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Thu, 2 Jul 2015 10:58:13 +0200 Subject: [PATCH 075/137] IDEA-142159 Settings | Keymap: adding and removing keyboard and mouse shortcuts is not saved MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KeymapImpl: clear myActionId2ListOfShortcuts on copy reduce memory usage — use our OrderedSet instead of LinkedHashSet part of IDEA-CR-3443 review --- .../ui/AbstractCollectionComboBoxModel.java | 2 +- .../openapi/keymap/impl/KeymapImpl.java | 60 +++-- .../openapi/keymap/impl/ui/ActionsTree.java | 53 ++-- .../impl/ui/KeyboardShortcutDialog.java | 11 +- .../openapi/keymap/impl/ui/KeymapPanel.java | 251 ++++++++---------- .../util/ui/CollectionModelEditor.java | 11 +- .../intellij/util/ui/ListModelEditorBase.java | 19 +- .../intellij/util/containers/OrderedSet.java | 15 +- 8 files changed, 205 insertions(+), 217 deletions(-) diff --git a/platform/platform-api/src/com/intellij/ui/AbstractCollectionComboBoxModel.java b/platform/platform-api/src/com/intellij/ui/AbstractCollectionComboBoxModel.java index 92329b428aa3..996ae90ea7b4 100644 --- a/platform/platform-api/src/com/intellij/ui/AbstractCollectionComboBoxModel.java +++ b/platform/platform-api/src/com/intellij/ui/AbstractCollectionComboBoxModel.java @@ -25,7 +25,7 @@ import java.util.Collection; * @author traff */ public abstract class AbstractCollectionComboBoxModel extends CollectionListModel implements ComboBoxModel { - private T mySelection; + protected T mySelection; public AbstractCollectionComboBoxModel(@Nullable T selection) { mySelection = selection; diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapImpl.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapImpl.java index 20b8af578520..2f69a039db5c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapImpl.java @@ -30,7 +30,9 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashMap; +import com.intellij.util.containers.OrderedSet; import gnu.trove.THashMap; +import gnu.trove.TObjectObjectProcedure; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -39,7 +41,6 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; -import java.awt.event.MouseEvent; import java.lang.reflect.Field; import java.util.*; @@ -77,7 +78,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap { private KeymapImpl myParent; private boolean myCanModify = true; - private final Map> myActionId2ListOfShortcuts = new THashMap>(); + private final THashMap> myActionId2ListOfShortcuts = new THashMap>(); /** * Don't use this field directly! Use it only through getKeystroke2ListOfIds. @@ -134,21 +135,26 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap { @NotNull public KeymapImpl copy() { - KeymapImpl newKeymap = new KeymapImpl(); - return copyTo(newKeymap); + return copyTo(new KeymapImpl()); } @NotNull - public KeymapImpl copyTo(@NotNull KeymapImpl otherKeymap) { + public KeymapImpl copyTo(@NotNull final KeymapImpl otherKeymap) { otherKeymap.myParent = myParent; otherKeymap.myName = myName; otherKeymap.myCanModify = canModify(); otherKeymap.cleanShortcutsCache(); - for (Map.Entry> entry : myActionId2ListOfShortcuts.entrySet()) { - otherKeymap.myActionId2ListOfShortcuts.put(entry.getKey(), new LinkedHashSet(entry.getValue())); - } + otherKeymap.myActionId2ListOfShortcuts.clear(); + otherKeymap.myActionId2ListOfShortcuts.ensureCapacity(myActionId2ListOfShortcuts.size()); + myActionId2ListOfShortcuts.forEachEntry(new TObjectObjectProcedure>() { + @Override + public boolean execute(String actionId, OrderedSet shortcuts) { + otherKeymap.myActionId2ListOfShortcuts.put(actionId, new OrderedSet(shortcuts)); + return true; + } + }); return otherKeymap; } @@ -195,9 +201,9 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap { } private void addShortcutSilently(String actionId, Shortcut shortcut, final boolean checkParentShortcut) { - LinkedHashSet list = myActionId2ListOfShortcuts.get(actionId); + OrderedSet list = myActionId2ListOfShortcuts.get(actionId); if (list == null) { - list = new LinkedHashSet(); + list = new OrderedSet(); myActionId2ListOfShortcuts.put(actionId, list); Shortcut[] boundShortcuts = getBoundShortcuts(actionId); if (boundShortcuts != null) { @@ -230,7 +236,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap { @Override public void removeShortcut(String actionId, Shortcut toDelete) { - LinkedHashSet list = myActionId2ListOfShortcuts.get(actionId); + OrderedSet list = myActionId2ListOfShortcuts.get(actionId); if (list != null) { Iterator it = list.iterator(); while (it.hasNext()) { @@ -253,7 +259,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap { if (inherited != null) { boolean affected = false; - LinkedHashSet newShortcuts = new LinkedHashSet(inherited.length); + OrderedSet newShortcuts = new OrderedSet(inherited.length); for (Shortcut eachInherited : inherited) { if (toDelete.equals(eachInherited)) { // skip this one @@ -306,7 +312,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap { } private void addAction2ShortcutsMap(final String actionId, final Map> strokesMap, final Class shortcutClass) { - LinkedHashSet listOfShortcuts = _getShortcuts(actionId); + OrderedSet listOfShortcuts = _getShortcuts(actionId); for (Shortcut shortcut : listOfShortcuts) { if (!shortcutClass.isAssignableFrom(shortcut.getClass())) { continue; @@ -328,7 +334,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap { } private void addKeystrokesMap(final String actionId, final Map> strokesMap) { - LinkedHashSet listOfShortcuts = _getShortcuts(actionId); + OrderedSet listOfShortcuts = _getShortcuts(actionId); for (Shortcut shortcut : listOfShortcuts) { if (!(shortcut instanceof KeyboardShortcut)) { continue; @@ -347,14 +353,14 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap { } } - private LinkedHashSet _getShortcuts(final String actionId) { + private OrderedSet _getShortcuts(final String actionId) { KeymapManagerEx keymapManager = getKeymapManager(); - LinkedHashSet listOfShortcuts = myActionId2ListOfShortcuts.get(actionId); + OrderedSet listOfShortcuts = myActionId2ListOfShortcuts.get(actionId); if (listOfShortcuts != null) { return listOfShortcuts; } else { - listOfShortcuts = new LinkedHashSet(); + listOfShortcuts = new OrderedSet(); } final String actionBinding = keymapManager.getActionBinding(actionId); @@ -519,7 +525,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap { @NotNull @Override public Shortcut[] getShortcuts(String actionId) { - LinkedHashSet shortcuts = myActionId2ListOfShortcuts.get(actionId); + OrderedSet shortcuts = myActionId2ListOfShortcuts.get(actionId); if (shortcuts == null) { Shortcut[] boundShortcuts = getBoundShortcuts(actionId); @@ -539,7 +545,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap { @Nullable private Shortcut[] getOwnShortcuts(String actionId) { - LinkedHashSet own = myActionId2ListOfShortcuts.get(actionId); + OrderedSet own = myActionId2ListOfShortcuts.get(actionId); if (own == null) return null; return own.isEmpty() ? ourEmptyShortcutsArray : own.toArray(new Shortcut[own.size()]); } @@ -685,7 +691,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap { } // Add read shortcuts for (String id : id2shortcuts.keySet()) { - myActionId2ListOfShortcuts.put(id, new LinkedHashSet(2)); // It's a trick! After that parent's shortcuts are not added to the keymap + myActionId2ListOfShortcuts.put(id, new OrderedSet(2)); // It's a trick! After that parent's shortcuts are not added to the keymap ArrayList shortcuts = id2shortcuts.get(id); for (Shortcut shortcut : shortcuts) { addShortcutSilently(id, shortcut, false); @@ -711,7 +717,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap { for (String actionId : ownActionIds) { Element actionElement = new Element(ACTION); actionElement.setAttribute(ID_ATTRIBUTE, actionId); - // Save keyboad shortcuts + // Save keyboard shortcuts Shortcut[] shortcuts = getShortcuts(actionId); for (Shortcut shortcut : shortcuts) { if (shortcut instanceof KeyboardShortcut) { @@ -805,23 +811,23 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap { // modifiers int modifiers = shortcut.getModifiers(); - if ((MouseEvent.SHIFT_DOWN_MASK & modifiers) > 0) { + if ((InputEvent.SHIFT_DOWN_MASK & modifiers) > 0) { buffer.append(SHIFT); buffer.append(' '); } - if ((MouseEvent.CTRL_DOWN_MASK & modifiers) > 0) { + if ((InputEvent.CTRL_DOWN_MASK & modifiers) > 0) { buffer.append(CONTROL); buffer.append(' '); } - if ((MouseEvent.META_DOWN_MASK & modifiers) > 0) { + if ((InputEvent.META_DOWN_MASK & modifiers) > 0) { buffer.append(META); buffer.append(' '); } - if ((MouseEvent.ALT_DOWN_MASK & modifiers) > 0) { + if ((InputEvent.ALT_DOWN_MASK & modifiers) > 0) { buffer.append(ALT); buffer.append(' '); } - if ((MouseEvent.ALT_GRAPH_DOWN_MASK & modifiers) > 0) { + if ((InputEvent.ALT_GRAPH_DOWN_MASK & modifiers) > 0) { buffer.append(ALT_GRAPH); buffer.append(' '); } @@ -849,7 +855,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap { cleanShortcutsCache(); } - public boolean hasOwnActionId(String actionId) { + public boolean hasOwnActionId(@NotNull String actionId) { return myActionId2ListOfShortcuts.containsKey(actionId); } diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/ActionsTree.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/ActionsTree.java index a10e6360d3c9..fdd72694b8bc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/ActionsTree.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/ActionsTree.java @@ -86,7 +86,6 @@ public class ActionsTree { if (rowBounds.intersects(clip)) { Object node = getPathForRow(row).getLastPathComponent(); - if (node instanceof DefaultMutableTreeNode) { Object data = ((DefaultMutableTreeNode)node).getUserObject(); Rectangle fullRowRect = new Rectangle(visibleRect.x, rowBounds.y, visibleRect.width, rowBounds.height); @@ -106,30 +105,20 @@ public class ActionsTree { @Override public void mouseMoved(MouseEvent e) { String description = getDescription(e); - if (description != null) { - ActionMenu.showDescriptionInStatusBar(true, myTree, description); - } - else { - ActionMenu.showDescriptionInStatusBar(false, myTree, null); - } + ActionMenu.showDescriptionInStatusBar(description != null, myTree, description); } @Nullable private String getDescription(@NotNull MouseEvent e) { TreePath path = myTree.getPathForLocation(e.getX(), e.getY()); - if (path == null) return null; + DefaultMutableTreeNode node = path == null ? null : (DefaultMutableTreeNode)path.getLastPathComponent(); + Object userObject = node == null ? null : node.getUserObject(); + if (!(userObject instanceof String)) { + return null; + } - DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent(); - if (node == null) return null; - - Object userObject = node.getUserObject(); - if (!(userObject instanceof String)) return null; - - String actionId = (String)userObject; - AnAction action = ActionManager.getInstance().getActionOrStub(actionId); - if (action == null) return null; - - return action.getTemplatePresentation().getDescription(); + AnAction action = ActionManager.getInstance().getActionOrStub((String)userObject); + return action == null ? null : action.getTemplatePresentation().getDescription(); } }); @@ -140,6 +129,11 @@ public class ActionsTree { ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); } + // silently replace current map + void setKeymap(@NotNull Keymap keymap) { + myKeymap = keymap; + } + public JComponent getComponent() { return myComponent; } @@ -171,7 +165,7 @@ public class ActionsTree { return (QuickList)userObject; } - public void reset(Keymap keymap, final QuickList[] allQuickLists) { + public void reset(@NotNull Keymap keymap, @NotNull QuickList[] allQuickLists) { reset(keymap, allQuickLists, myFilter, null); } @@ -188,7 +182,7 @@ public class ActionsTree { reset(myKeymap, currentQuickListIds, filter, null); } - private void reset(final Keymap keymap, final QuickList[] allQuickLists, String filter, @Nullable KeyboardShortcut shortcut) { + private void reset(@NotNull Keymap keymap, @NotNull QuickList[] allQuickLists, String filter, @Nullable KeyboardShortcut shortcut) { myKeymap = keymap; final PathsKeeper pathsKeeper = new PathsKeeper(); @@ -198,11 +192,11 @@ public class ActionsTree { ActionManager actionManager = ActionManager.getInstance(); Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(myComponent)); - Group mainGroup = ActionsTreeUtil.createMainGroup(project, myKeymap, allQuickLists, filter, true, - ActionsTreeUtil.isActionFiltered(actionManager, myKeymap, shortcut, filter, true)); + Group mainGroup = ActionsTreeUtil.createMainGroup(project, keymap, allQuickLists, filter, true, + ActionsTreeUtil.isActionFiltered(actionManager, keymap, shortcut, filter, true)); if ((filter != null && filter.length() > 0 || shortcut != null) && mainGroup.initIds().isEmpty()){ - mainGroup = ActionsTreeUtil.createMainGroup(project, myKeymap, allQuickLists, filter, false, - ActionsTreeUtil.isActionFiltered(actionManager, myKeymap, shortcut, filter, false)); + mainGroup = ActionsTreeUtil.createMainGroup(project, keymap, allQuickLists, filter, false, + ActionsTreeUtil.isActionFiltered(actionManager, keymap, shortcut, filter, false)); } myRoot = ActionsTreeUtil.createNode(mainGroup); myMainGroup = mainGroup; @@ -251,14 +245,7 @@ public class ActionsTree { if (userObject instanceof QuickList) { userObject = ((QuickList)userObject).getActionId(); } - - if (userObject instanceof String) { - Shortcut[] shortcuts = myKeymap.getShortcuts((String)userObject); - return KeymapUtil.getShortcutsText(shortcuts); - } - else { - return ""; - } + return userObject instanceof String ? KeymapUtil.getShortcutsText(myKeymap.getShortcuts((String)userObject)) : ""; } else { return "???"; diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeyboardShortcutDialog.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeyboardShortcutDialog.java index 0894b0dd7afc..b08cf0fdd087 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeyboardShortcutDialog.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeyboardShortcutDialog.java @@ -34,6 +34,7 @@ import com.intellij.util.ArrayUtil; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.border.EmptyBorder; @@ -214,7 +215,8 @@ public class KeyboardShortcutDialog extends DialogWrapper { myConflictInfoArea.setForeground(JBColor.RED); if (loaded) { myConflictInfoArea.setText(KeyMapBundle.message("assigned.to.info.message", buffer.toString())); - } else { + } + else { myConflictInfoArea.setText("Assigned to " + buffer.toString() + " which is now not loaded but may be loaded later"); } } @@ -224,13 +226,10 @@ public class KeyboardShortcutDialog extends DialogWrapper { mySecondStrokePanel.setEnabled(myEnableSecondKeystroke.isSelected()); } + @Nullable public KeyboardShortcut getKeyboardShortcut() { KeyStroke firstStroke = myFirstStrokePanel.getKeyStroke(); - if (firstStroke == null) { - return null; - } - KeyStroke secondStroke = myEnableSecondKeystroke.isSelected() ? mySecondStrokePanel.getKeyStroke() : null; - return new KeyboardShortcut(firstStroke, secondStroke); + return firstStroke == null ? null : new KeyboardShortcut(firstStroke, myEnableSecondKeystroke.isSelected() ? mySecondStrokePanel.getKeyStroke() : null); } static String getTextByKeyStroke(KeyStroke keyStroke) { diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeymapPanel.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeymapPanel.java index 36a5af720474..3df7eb33ef35 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeymapPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeymapPanel.java @@ -119,8 +119,6 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi } }); - private Keymap mySelectedKeymap; - private JButton myCopyButton; private JButton myDeleteButton; private JButton myResetToDefault; @@ -146,7 +144,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi @Override public void propertyChange(@NotNull final PropertyChangeEvent evt) { if (evt.getPropertyName().equals("ancestor") && evt.getNewValue() != null && evt.getOldValue() == null && myQuickListsModified) { - processCurrentKeymapChanged(myQuickLists); + currentKeymapChanged(); myQuickListsModified = false; } } @@ -185,9 +183,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi myEditor.getComboBox().addActionListener(new ActionListener() { @Override public void actionPerformed(@NotNull ActionEvent e) { - if (myEditor.getModel().getSelectedItem() != mySelectedKeymap) { - processCurrentKeymapChanged(myQuickLists); - } + currentKeymapChanged(); } }); panel.add(createKeymapNamePanel(), new GridBagConstraints(3, 0, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 10, 0, 0), 0, 0)); @@ -207,10 +203,13 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi @Override public void processCurrentKeymapChanged(@NotNull QuickList[] ids) { myQuickLists = ids; + currentKeymapChanged(); + } + + private void currentKeymapChanged() { myResetToDefault.setEnabled(false); Keymap selectedKeymap = myEditor.getModel().getSelected(); - mySelectedKeymap = selectedKeymap; boolean editable = selectedKeymap != null && selectedKeymap.canModify(); myDeleteButton.setEnabled(editable); @@ -228,16 +227,12 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi } else { myBaseKeymapLabel.setText(KeyMapBundle.message("based.on.keymap.label", parent.getPresentableName())); - if (selectedKeymap.canModify() && ((KeymapImpl)mySelectedKeymap).getOwnActionIds().length > 0) { + if (selectedKeymap.canModify() && ((KeymapImpl)selectedKeymap).getOwnActionIds().length > 0) { myResetToDefault.setEnabled(true); } } - resetActionsTree(selectedKeymap); - } - - private void resetActionsTree(@NotNull Keymap keymap) { - myActionsTree.reset(myEditor.getMutable(keymap), myQuickLists); + myActionsTree.reset(selectedKeymap, myQuickLists); } private JPanel createKeymapButtonsPanel() { @@ -450,7 +445,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi private JPanel createFilteringPanel() { //noinspection ConstantConditions - resetActionsTree(myEditor.getModel().getSelected()); + myActionsTree.reset(myEditor.getModel().getSelected(), myQuickLists); final JLabel firstLabel = new JLabel(KeyMapBundle.message("filter.first.stroke.input")); final JCheckBox enable2Shortcut = new JCheckBox(KeyMapBundle.message("filter.second.stroke.input")); @@ -482,12 +477,11 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi }); IJSwingUtilities.adjustComponentsOnMac(firstLabel, firstShortcut); - //noinspection deprecation JPanel filterComponent = FormBuilder.createFormBuilder() .addLabeledComponent(firstLabel, firstShortcut, true) .addComponent(enable2Shortcut) .setVerticalGap(0) - .setIndent(5) + .setHorizontalGap(5) .addComponent(secondShortcut) .getPanel(); @@ -525,31 +519,21 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi myActionsTree.filter(option, myQuickLists); } - private void addKeyboardShortcut(Shortcut shortcut) { - String actionId = myActionsTree.getSelectedActionId(); - if (actionId == null) { - return; - } - - if (!createKeymapCopyIfNeeded()) return; + private void addKeyboardShortcut(@NotNull String actionId, @Nullable Shortcut shortcut) { + Keymap keymap = createKeymapCopyIfNeeded(); KeyboardShortcutDialog dialog = new KeyboardShortcutDialog(this, actionId, myQuickLists); - - - KeyboardShortcut selectedKeyboardShortcut = shortcut instanceof KeyboardShortcut ? (KeyboardShortcut)shortcut : null; - - dialog.setData(mySelectedKeymap, selectedKeyboardShortcut); + dialog.setData(keymap, shortcut instanceof KeyboardShortcut ? (KeyboardShortcut)shortcut : null); if (!dialog.showAndGet()) { return; } KeyboardShortcut keyboardShortcut = dialog.getKeyboardShortcut(); - if (keyboardShortcut == null) { return; } - Map> conflicts = mySelectedKeymap.getConflicts(actionId, keyboardShortcut); + Map> conflicts = keymap.getConflicts(actionId, keyboardShortcut); if (!conflicts.isEmpty()) { int result = Messages.showYesNoCancelDialog( this, @@ -563,7 +547,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi if (result == Messages.YES) { for (String id : conflicts.keySet()) { for (KeyboardShortcut s : conflicts.get(id)) { - mySelectedKeymap.removeShortcut(id, s); + keymap.removeShortcut(id, s); } } } @@ -573,20 +557,20 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi } // if shortcut is already registered to this action, just select it in the list - Shortcut[] shortcuts = mySelectedKeymap.getShortcuts(actionId); + Shortcut[] shortcuts = keymap.getShortcuts(actionId); for (Shortcut s : shortcuts) { if (s.equals(keyboardShortcut)) { return; } } - mySelectedKeymap.addShortcut(actionId, keyboardShortcut); + keymap.addShortcut(actionId, keyboardShortcut); if (StringUtil.startsWithChar(actionId, '$')) { - mySelectedKeymap.addShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), keyboardShortcut); + keymap.addShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), keyboardShortcut); } repaintLists(); - processCurrentKeymapChanged(myQuickLists); + currentKeymapChanged(); } private void addMouseShortcut(Shortcut shortcut, ShortcutRestrictions restrictions) { @@ -595,14 +579,14 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi return; } - if (!createKeymapCopyIfNeeded()) return; + Keymap keymap = createKeymapCopyIfNeeded(); MouseShortcut mouseShortcut = shortcut instanceof MouseShortcut ? (MouseShortcut)shortcut : null; MouseShortcutDialog dialog = new MouseShortcutDialog( this, mouseShortcut, - mySelectedKeymap, + keymap, actionId, myActionsTree.getMainGroup(), restrictions @@ -617,7 +601,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi return; } - String[] actionIds = mySelectedKeymap.getActionIds(mouseShortcut); + String[] actionIds = keymap.getActionIds(mouseShortcut); if (actionIds.length > 1 || (actionIds.length == 1 && !actionId.equals(actionIds[0]))) { int result = Messages.showYesNoCancelDialog( this, @@ -630,7 +614,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi if (result == Messages.YES) { for (String id : actionIds) { - mySelectedKeymap.removeShortcut(id, mouseShortcut); + keymap.removeShortcut(id, mouseShortcut); } } else if (result != Messages.NO) { @@ -640,50 +624,51 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi // if shortcut is already registered to this action, just select it in the list - Shortcut[] shortcuts = mySelectedKeymap.getShortcuts(actionId); + Shortcut[] shortcuts = keymap.getShortcuts(actionId); for (Shortcut shortcut1 : shortcuts) { if (shortcut1.equals(mouseShortcut)) { return; } } - mySelectedKeymap.addShortcut(actionId, mouseShortcut); + keymap.addShortcut(actionId, mouseShortcut); if (StringUtil.startsWithChar(actionId, '$')) { - mySelectedKeymap.addShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), mouseShortcut); + keymap.addShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), mouseShortcut); } repaintLists(); - processCurrentKeymapChanged(myQuickLists); + currentKeymapChanged(); } private void repaintLists() { myActionsTree.getComponent().repaint(); } - private boolean createKeymapCopyIfNeeded() { - if (mySelectedKeymap.canModify()) return true; - - final Keymap selectedKeymap = myEditor.getModel().getSelected(); - if (selectedKeymap == null) { - return false; + @NotNull + private Keymap createKeymapCopyIfNeeded() { + Keymap keymap = myEditor.getModel().getSelected(); + assert keymap != null; + if (keymap.canModify()) { + Keymap mutable = myEditor.getMutable(keymap); + myActionsTree.setKeymap(mutable); + return mutable; } - KeymapImpl newKeymap = ((KeymapImpl)selectedKeymap).deriveKeymap(); - - String newKeymapName = KeyMapBundle.message("new.keymap.name", selectedKeymap.getPresentableName()); + String newKeymapName = KeyMapBundle.message("new.keymap.name", keymap.getPresentableName()); if (!tryNewKeymapName(newKeymapName)) { for (int i = 0; ; i++) { - newKeymapName = KeyMapBundle.message("new.indexed.keymap.name", selectedKeymap.getPresentableName(), i); + newKeymapName = KeyMapBundle.message("new.indexed.keymap.name", keymap.getPresentableName(), i); if (tryNewKeymapName(newKeymapName)) { break; } } } + KeymapImpl newKeymap = ((KeymapImpl)keymap).deriveKeymap(); newKeymap.setName(newKeymapName); newKeymap.setCanModify(true); - final int indexOf = myEditor.getModel().getElementIndex(selectedKeymap); + int indexOf = myEditor.getModel().getElementIndex(keymap); if (indexOf >= 0) { myEditor.getModel().add(indexOf + 1, newKeymap); } @@ -692,28 +677,8 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi } myEditor.getModel().setSelectedItem(newKeymap); - processCurrentKeymapChanged(myQuickLists); - - return true; - } - - private void removeShortcut(Shortcut shortcut) { - String actionId = myActionsTree.getSelectedActionId(); - if (actionId == null) { - return; - } - - if (!createKeymapCopyIfNeeded()) return; - - if (shortcut == null) return; - - mySelectedKeymap.removeShortcut(actionId, shortcut); - if (StringUtil.startsWithChar(actionId, '$')) { - mySelectedKeymap.removeShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), shortcut); - } - - repaintLists(); - processCurrentKeymapChanged(myQuickLists); + currentKeymapChanged(); + return newKeymap; } private void copyKeymap() { @@ -738,7 +703,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi myEditor.getModel().add(newKeymap); myEditor.getModel().setSelectedItem(newKeymap); myEditor.getComboBox().getEditor().selectAll(); - processCurrentKeymapChanged(myQuickLists); + currentKeymapChanged(); } private boolean tryNewKeymapName(String name) { @@ -759,7 +724,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi } myEditor.getModel().remove(keymap); - processCurrentKeymapChanged(myQuickLists); + currentKeymapChanged(); } private void resetKeymap() { @@ -768,7 +733,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi return; } ((KeymapImpl)keymap).clearOwnActionsIds(); - processCurrentKeymapChanged(myQuickLists); + currentKeymapChanged(); } @Override @@ -784,11 +749,11 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi myNonEnglishKeyboardSupportOption.setSelected(KeyboardSettingsExternalizable.getInstance().isNonEnglishKeyboardSupportEnabled()); } - mySelectedKeymap = null; + Keymap selectedKeymap = null; List list = getManagerKeymaps(); for (Keymap keymap : list) { - if (mySelectedKeymap == null && keymap == KeymapManagerEx.getInstanceEx().getActiveKeymap()) { - mySelectedKeymap = keymap; + if (selectedKeymap == null && keymap == KeymapManagerEx.getInstanceEx().getActiveKeymap()) { + selectedKeymap = keymap; } } myEditor.reset(list); @@ -797,23 +762,23 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi KeymapImpl keymap = new KeymapImpl(); keymap.setName(KeyMapBundle.message("keymap.no.name")); myEditor.getModel().add(keymap); - mySelectedKeymap = keymap; + selectedKeymap = keymap; } - myEditor.getModel().setSelectedItem(mySelectedKeymap); + myEditor.getModel().setSelectedItem(selectedKeymap); - processCurrentKeymapChanged(myQuickLists); + currentKeymapChanged(); } @Override public void apply() throws ConfigurationException { - myEditor.ensureNonEmptyNames("Quick list should have non empty name"); + myEditor.ensureNonEmptyNames(KeyMapBundle.message("configuration.all.keymaps.should.have.non.empty.names.error.message")); ensureUniqueKeymapNames(); KeymapManagerImpl keymapManager = (KeymapManagerImpl)KeymapManager.getInstance(); // we must specify the same filter, which was used to get original items keymapManager.setKeymaps(myEditor.apply(), KEYMAP_FILTER); - keymapManager.setActiveKeymap(mySelectedKeymap); + keymapManager.setActiveKeymap(myEditor.getModel().getSelected()); ActionToolbarImpl.updateAllToolbarsImmediately(); } @@ -828,7 +793,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi @Override public boolean isModified() { - return !Comparing.equal(mySelectedKeymap, KeymapManager.getInstance().getActiveKeymap()) || myEditor.isModified(); + return !Comparing.equal(myEditor.getModel().getSelected(), KeymapManager.getInstance().getActiveKeymap()) || myEditor.isModified(); } @NotNull @@ -873,34 +838,59 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi @Nullable public Shortcut[] getCurrentShortcuts(@NotNull String actionId) { - return mySelectedKeymap == null ? null : mySelectedKeymap.getShortcuts(actionId); + Keymap keymap = myEditor.getModel().getSelected(); + return keymap == null ? null : keymap.getShortcuts(actionId); } private void editSelection(InputEvent e) { - final String actionId = myActionsTree.getSelectedActionId(); - if (actionId == null) return; + String actionId = myActionsTree.getSelectedActionId(); + if (actionId == null) { + return; + } + DefaultActionGroup group = createEditActionGroup(actionId); + if (e instanceof MouseEvent && ((MouseEvent)e).isPopupTrigger()) { + ActionManager.getInstance() + .createActionPopupMenu(ActionPlaces.UNKNOWN, group) + .getComponent() + .show(e.getComponent(), ((MouseEvent)e).getX(), ((MouseEvent)e).getY()); + } + else { + DataContext dataContext = DataManager.getInstance().getDataContext(this); + ListPopup popup = JBPopupFactory.getInstance().createActionGroupPopup("Edit Shortcuts", + group, + dataContext, + JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, + true); + + if (e instanceof MouseEvent) { + popup.show(new RelativePoint((MouseEvent)e)); + } + else { + popup.showInBestPositionFor(dataContext); + } + } + } + + @NotNull + private DefaultActionGroup createEditActionGroup(@NotNull final String actionId) { DefaultActionGroup group = new DefaultActionGroup(); - - final Shortcut[] shortcuts = getCurrentShortcuts(actionId); - final Set abbreviations = AbbreviationManager.getInstance().getAbbreviations(actionId); - final ShortcutRestrictions restrictions = ActionShortcutRestrictions.getInstance().getForActionId(actionId); - if (restrictions.allowKeyboardShortcut) { group.add(new DumbAwareAction("Add Keyboard Shortcut") { @Override public void actionPerformed(@NotNull AnActionEvent e) { - Shortcut firstKeyboard = null; - assert shortcuts != null; - for (Shortcut shortcut : shortcuts) { + Shortcut firstShortcut = null; + Keymap keymap = myEditor.getModel().getSelected(); + assert keymap != null; + for (Shortcut shortcut : keymap.getShortcuts(actionId)) { if (shortcut instanceof KeyboardShortcut) { - firstKeyboard = shortcut; + firstShortcut = shortcut; break; } } - addKeyboardShortcut(firstKeyboard); + addKeyboardShortcut(actionId, firstShortcut); } }); } @@ -910,8 +900,9 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi @Override public void actionPerformed(@NotNull AnActionEvent e) { Shortcut firstMouse = null; - assert shortcuts != null; - for (Shortcut shortcut : shortcuts) { + Keymap keymap = myEditor.getModel().getSelected(); + assert keymap != null; + for (Shortcut shortcut : keymap.getShortcuts(actionId)) { if (shortcut instanceof MouseShortcut) { firstMouse = shortcut; break; @@ -926,10 +917,9 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi group.add(new DumbAwareAction("Add Abbreviation") { @Override public void actionPerformed(@NotNull AnActionEvent e) { - final String abbr = Messages.showInputDialog("Enter new abbreviation:", "Abbreviation", null); + String abbr = Messages.showInputDialog("Enter new abbreviation:", "Abbreviation", null); if (abbr != null) { - String actionId = myActionsTree.getSelectedActionId(); - AbbreviationManager.getInstance().register(abbr, actionId); + AbbreviationManager.getInstance().register(abbr, myActionsTree.getSelectedActionId()); repaintLists(); } } @@ -943,29 +933,32 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi group.addSeparator(); - assert shortcuts != null; - for (final Shortcut shortcut : shortcuts) { + Keymap keymap = myEditor.getModel().getSelected(); + assert keymap != null; + for (final Shortcut shortcut : keymap.getShortcuts(actionId)) { group.add(new DumbAwareAction("Remove " + KeymapUtil.getShortcutText(shortcut)) { @Override public void actionPerformed(@NotNull AnActionEvent e) { - removeShortcut(shortcut); + Keymap keymap = createKeymapCopyIfNeeded(); + keymap.removeShortcut(actionId, shortcut); + if (StringUtil.startsWithChar(actionId, '$')) { + keymap.removeShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), shortcut); + } + + repaintLists(); + currentKeymapChanged(); } }); } if (Registry.is("actionSystem.enableAbbreviations")) { - for (final String abbreviation : abbreviations) { + for (final String abbreviation : AbbreviationManager.getInstance().getAbbreviations(actionId)) { group.addAction(new DumbAwareAction("Remove Abbreviation '" + abbreviation + "'") { @Override public void actionPerformed(@NotNull AnActionEvent e) { AbbreviationManager.getInstance().remove(abbreviation, actionId); repaintLists(); } - - @Override - public void update(@NotNull AnActionEvent e) { - super.update(e); - } }); } } @@ -973,37 +966,17 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi group.add(new DumbAwareAction("Reset Shortcuts") { @Override public void actionPerformed(@NotNull AnActionEvent e) { - ((KeymapImpl)mySelectedKeymap).clearOwnActionsId(actionId); - processCurrentKeymapChanged(myQuickLists); + ((KeymapImpl)createKeymapCopyIfNeeded()).clearOwnActionsId(actionId); + currentKeymapChanged(); repaintLists(); } @Override public void update(@NotNull AnActionEvent e) { - e.getPresentation().setVisible(mySelectedKeymap.canModify() && ((KeymapImpl)mySelectedKeymap).hasOwnActionId(actionId)); - super.update(e); + e.getPresentation().setVisible(((KeymapImpl)myEditor.getModel().getSelected()).hasOwnActionId(actionId)); } }); - if (e instanceof MouseEvent && ((MouseEvent)e).isPopupTrigger()) { - final ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu(ActionPlaces.UNKNOWN, group); - popupMenu.getComponent().show(e.getComponent(), ((MouseEvent)e).getX(), ((MouseEvent)e).getY()); - } - else { - final DataContext dataContext = DataManager.getInstance().getDataContext(this); - final ListPopup popup = JBPopupFactory.getInstance() - .createActionGroupPopup("Edit Shortcuts", - group, - dataContext, - JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, - true); - - if (e instanceof MouseEvent) { - popup.show(new RelativePoint((MouseEvent)e)); - } - else { - popup.showInBestPositionFor(dataContext); - } - } + return group; } } diff --git a/platform/platform-impl/src/com/intellij/util/ui/CollectionModelEditor.java b/platform/platform-impl/src/com/intellij/util/ui/CollectionModelEditor.java index 395327aa653c..f1dd95078ec2 100644 --- a/platform/platform-impl/src/com/intellij/util/ui/CollectionModelEditor.java +++ b/platform/platform-impl/src/com/intellij/util/ui/CollectionModelEditor.java @@ -130,10 +130,7 @@ public abstract class CollectionModelEditor mutable = itemEditor.clone(item, false); modifiedToOriginal.put(mutable, item); originalToModified.put(item, mutable); - - List items = getItems(); - // silently replace item - items.set(index == -1 ? ContainerUtil.indexOfIdentity(items, item) : index, mutable); + silentlyReplaceItem(item, mutable, index); } return mutable; } @@ -148,6 +145,12 @@ public abstract class CollectionModelEditor } } + protected void silentlyReplaceItem(@NotNull T oldItem, @NotNull T newItem, int index) { + // silently replace item + List items = getItems(); + items.set(index == -1 ? ContainerUtil.indexOfIdentity(items, oldItem) : index, newItem); + } + protected final boolean areSelectedItemsRemovable(@NotNull ListSelectionModel selectionMode) { int minSelectionIndex = selectionMode.getMinSelectionIndex(); int maxSelectionIndex = selectionMode.getMaxSelectionIndex(); diff --git a/platform/platform-impl/src/com/intellij/util/ui/ListModelEditorBase.java b/platform/platform-impl/src/com/intellij/util/ui/ListModelEditorBase.java index d46b94b73445..6ba98bf6648b 100644 --- a/platform/platform-impl/src/com/intellij/util/ui/ListModelEditorBase.java +++ b/platform/platform-impl/src/com/intellij/util/ui/ListModelEditorBase.java @@ -19,7 +19,6 @@ import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.CollectionListModel; import com.intellij.ui.MutableCollectionComboBoxModel; -import com.intellij.util.containers.ContainerUtil; import gnu.trove.TObjectObjectProcedure; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -85,11 +84,7 @@ public abstract class ListModelEditorBase extends CollectionModelEditor extends CollectionModelEditor { @NotNull final List items() { return super.getInternalList(); } + void checkSelectionOnSilentReplace(@NotNull T oldItem, @NotNull T newItem) { + if (mySelection == oldItem) { + mySelection = newItem; + } + } + @Override protected void itemReplaced(@NotNull T existingItem, @Nullable T newItem) { helper.remove(existingItem); diff --git a/platform/util/src/com/intellij/util/containers/OrderedSet.java b/platform/util/src/com/intellij/util/containers/OrderedSet.java index 0ff04acbe345..e5ac58899dd9 100644 --- a/platform/util/src/com/intellij/util/containers/OrderedSet.java +++ b/platform/util/src/com/intellij/util/containers/OrderedSet.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. @@ -29,6 +29,13 @@ public class OrderedSet extends ArrayList implements Set, RandomAccess this(ContainerUtil.canonicalStrategy()); } + public OrderedSet(@NotNull Collection set) { + super(set.size()); + + myHashSet = new OpenTHashSet(set.size()); + addAll(set); + } + public OrderedSet(@NotNull TObjectHashingStrategy hashingStrategy) { this(hashingStrategy, 4); } @@ -38,6 +45,12 @@ public class OrderedSet extends ArrayList implements Set, RandomAccess myHashSet = new OpenTHashSet(capacity, hashingStrategy); } + public OrderedSet(int capacity) { + super(capacity); + + myHashSet = new OpenTHashSet(capacity); + } + @Override public boolean removeAll(@NotNull Collection c) { boolean removed = false; From 26c6d6157fc70ab37261b47d4cfef69141eb054f Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Thu, 2 Jul 2015 13:51:16 +0200 Subject: [PATCH 076/137] cleanup --- .../protocol-model-generator/src/Generator.kt | 8 ++++---- .../protocol/protocol-model-generator/src/NamePath.kt | 2 +- .../protocol/protocol-model-generator/src/TypeMap.kt | 11 ++++------- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/platform/script-debugger/protocol/protocol-model-generator/src/Generator.kt b/platform/script-debugger/protocol/protocol-model-generator/src/Generator.kt index a120d1267904..121d0428151d 100644 --- a/platform/script-debugger/protocol/protocol-model-generator/src/Generator.kt +++ b/platform/script-debugger/protocol/protocol-model-generator/src/Generator.kt @@ -88,7 +88,7 @@ class Generator(outputDir: String, rootPackage: String, requestClassName: String } fun resolveType(typedObject: ItemDescriptor, scope: ResolveAndGenerateScope): TypeDescriptor { - val optional = typedObject is ItemDescriptor.Named && (typedObject : ItemDescriptor.Named).optional() + val optional = typedObject is ItemDescriptor.Named && typedObject.optional() return switchByType(typedObject, object : TypeVisitor { override fun visitRef(refName: String): TypeDescriptor { return TypeDescriptor(resolveRefType(scope.getDomainName(), refName, scope.getTypeDirection()), optional) @@ -120,7 +120,7 @@ class Generator(outputDir: String, rootPackage: String, requestClassName: String } override fun visitArray(items: ProtocolMetaModel.ArrayItemType): TypeDescriptor { - val type = scope.resolveType(items).type + val type = scope.resolveType(items).type return TypeDescriptor(ListType(type), optional, false, type == BoxableType.ANY_STRING) } @@ -244,7 +244,7 @@ fun capitalizeFirstChar(s: String): String { } fun switchByType(typedObject: ItemDescriptor, visitor: TypeVisitor): R { - val refName = if (typedObject is ItemDescriptor.Referenceable) (typedObject : ItemDescriptor.Referenceable).ref() else null + val refName = if (typedObject is ItemDescriptor.Referenceable) typedObject.ref() else null if (refName != null) { return visitor.visitRef(refName) } @@ -265,7 +265,7 @@ fun switchByType(typedObject: ItemDescriptor, visitor: TypeVisitor): R { return visitor.visitObject(null) } - val properties = (typedObject : ItemDescriptor.Type).properties() + val properties = typedObject.properties() if (properties == null || properties.isEmpty()) { return visitor.visitMap() } diff --git a/platform/script-debugger/protocol/protocol-model-generator/src/NamePath.kt b/platform/script-debugger/protocol/protocol-model-generator/src/NamePath.kt index 92760b3cf22f..aab52a4fa349 100644 --- a/platform/script-debugger/protocol/protocol-model-generator/src/NamePath.kt +++ b/platform/script-debugger/protocol/protocol-model-generator/src/NamePath.kt @@ -7,7 +7,7 @@ data class NamePath(val lastComponent: String, val parent: NamePath? = null) { var current: NamePath? = this while (current != null) { res++ - current = current!!.parent + current = current.parent } } return res diff --git a/platform/script-debugger/protocol/protocol-model-generator/src/TypeMap.kt b/platform/script-debugger/protocol/protocol-model-generator/src/TypeMap.kt index 26bd05474c9d..7fb40a76594d 100644 --- a/platform/script-debugger/protocol/protocol-model-generator/src/TypeMap.kt +++ b/platform/script-debugger/protocol/protocol-model-generator/src/TypeMap.kt @@ -15,10 +15,7 @@ class TypeMap { private val typesToGenerate = ArrayList() fun resolve(domainName: String, typeName: String, direction: TypeData.Direction): BoxableType? { - val domainGenerator = domainGeneratorMap!!.get(domainName) - if (domainGenerator == null) { - throw RuntimeException("Failed to find domain generator: " + domainName) - } + val domainGenerator = domainGeneratorMap!!.get(domainName) ?: throw RuntimeException("Failed to find domain generator: " + domainName) return direction.get(getTypeData(domainName, typeName)).resolve(this, domainGenerator) } @@ -28,7 +25,7 @@ class TypeMap { fun generateRequestedTypes() { // size may grow during iteration - var list = typesToGenerate.copyToArray() + var list = typesToGenerate.toTypedArray() typesToGenerate.clear() while (true) { for (binding in list) { @@ -39,7 +36,7 @@ class TypeMap { break } else { - list = typesToGenerate.copyToArray() + list = typesToGenerate.toTypedArray() typesToGenerate.clear() } } @@ -52,6 +49,6 @@ class TypeMap { result = TypeData(typeName) map.put(key, result) } - return result!! + return result } } From cc1a85f7b307a3a786b102419e4bb81044b5332d Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Thu, 2 Jul 2015 14:15:33 +0200 Subject: [PATCH 077/137] IDEA-142128 When Preview tab is open, Find button navigates to the first found fragment --- .../lang-impl/src/com/intellij/find/impl/FindDialog.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java b/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java index 9946b3795e47..ba00d930e2a3 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java @@ -669,15 +669,6 @@ public class FindDialog extends DialogWrapper { } private void doOKAction(boolean findAll) { - if (myResultsPreviewTable != null && - myContent.getSelectedIndex() == RESULTS_PREVIEW_TAB_INDEX && - myResultsPreviewTable.getSelectedRowCount() != 0 && - !myModel.isReplaceState() - ) { - navigateToSelectedUsage(myResultsPreviewTable); - return; - } - FindModel validateModel = myModel.clone(); applyTo(validateModel, findAll); From e737db6dcc31c3195b31848d6e719a3e7d91c9e7 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 2 Jul 2015 15:29:10 +0300 Subject: [PATCH 078/137] java: receiver parameter parsing --- .../intellij/psi/PsiReceiverParameter.java | 4 + .../lang/java/parser/DeclarationParser.java | 16 ++ .../impl/source/PsiReceiverParameterImpl.java | 13 +- .../parser-partial/declarations/Receiver.txt | 178 ++++++++++++++++++ .../parser/partial/DeclarationParserTest.java | 9 + .../com/intellij/psi/JavaStubBuilderTest.java | 2 +- 6 files changed, 213 insertions(+), 9 deletions(-) create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/Receiver.txt diff --git a/java/java-psi-api/src/com/intellij/psi/PsiReceiverParameter.java b/java/java-psi-api/src/com/intellij/psi/PsiReceiverParameter.java index eb93e359f575..abbbb4fbd52c 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiReceiverParameter.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiReceiverParameter.java @@ -15,10 +15,14 @@ */ package com.intellij.psi; +import org.jetbrains.annotations.NotNull; + /** * Represents a receiver parameter of a Java method (Java 8). * * @since 12.1 */ public interface PsiReceiverParameter extends PsiVariable { + @NotNull + PsiThisExpression getIdentifier(); } diff --git a/java/java-psi-impl/src/com/intellij/lang/java/parser/DeclarationParser.java b/java/java-psi-impl/src/com/intellij/lang/java/parser/DeclarationParser.java index 634eda49bcdc..d916d1d1cf4a 100644 --- a/java/java-psi-impl/src/com/intellij/lang/java/parser/DeclarationParser.java +++ b/java/java-psi-impl/src/com/intellij/lang/java/parser/DeclarationParser.java @@ -608,6 +608,22 @@ public class DeclarationParser { } } + if (typed) { + IElementType tokenType = builder.getTokenType(); + if (tokenType == JavaTokenType.THIS_KEYWORD || tokenType == JavaTokenType.IDENTIFIER && builder.lookAhead(1) == JavaTokenType.DOT) { + PsiBuilder.Marker mark = builder.mark(); + + PsiBuilder.Marker expr = myParser.getExpressionParser().parse(builder); + if (expr != null && exprType(expr) == JavaElementType.THIS_EXPRESSION) { + mark.drop(); + done(param, JavaElementType.RECEIVER_PARAMETER); + return param; + } + + mark.rollbackTo(); + } + } + if (expect(builder, JavaTokenType.IDENTIFIER)) { if (!resource) { eatBrackets(builder, typeInfo != null && typeInfo.isVarArg ? "expected.rparen" : null); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiReceiverParameterImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiReceiverParameterImpl.java index eb26691f059d..05cbfa82c136 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiReceiverParameterImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiReceiverParameterImpl.java @@ -15,7 +15,6 @@ */ package com.intellij.psi.impl.source; -import com.intellij.lang.ASTNode; import com.intellij.psi.*; import com.intellij.psi.impl.source.tree.CompositePsiElement; import com.intellij.psi.impl.source.tree.JavaElementType; @@ -30,6 +29,11 @@ public class PsiReceiverParameterImpl extends CompositePsiElement implements Psi super(JavaElementType.RECEIVER_PARAMETER); } + @NotNull + public PsiThisExpression getIdentifier() { + return PsiTreeUtil.getRequiredChildOfType(this, PsiThisExpression.class); + } + @Nullable @Override public PsiModifierList getModifierList() { @@ -100,13 +104,6 @@ public class PsiReceiverParameterImpl extends CompositePsiElement implements Psi return getIdentifier().getTextOffset(); } - @NotNull - private PsiElement getIdentifier() { - ASTNode node = findChildByType(JavaTokenType.THIS_KEYWORD); - assert node != null : getChildren(); - return node.getPsi(); - } - @Override public String toString() { return "PsiReceiverParameter"; diff --git a/java/java-tests/testData/psi/parser-partial/declarations/Receiver.txt b/java/java-tests/testData/psi/parser-partial/declarations/Receiver.txt new file mode 100644 index 000000000000..aa0e63c9ee3e --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/Receiver.txt @@ -0,0 +1,178 @@ +PsiJavaFile:Receiver.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiMethod:m1 + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:void + PsiKeyword:void('void') + PsiWhiteSpace(' ') + PsiIdentifier:m1('m1') + PsiParameterList:(C this) + PsiJavaToken:LPARENTH('(') + PsiReceiverParameter + PsiModifierList: + + PsiTypeElement:C + PsiJavaCodeReferenceElement:C + PsiIdentifier:C('C') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiThisExpression:this + PsiReferenceParameterList + + PsiKeyword:this('this') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiMethod:m2 + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:void + PsiKeyword:void('void') + PsiWhiteSpace(' ') + PsiIdentifier:m2('m2') + PsiParameterList:(T T.this) + PsiJavaToken:LPARENTH('(') + PsiReceiverParameter + PsiModifierList: + + PsiTypeElement:T + PsiJavaCodeReferenceElement:T + PsiIdentifier:T('T') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiThisExpression:T.this + PsiJavaCodeReferenceElement:T + PsiIdentifier:T('T') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiKeyword:this('this') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiMethod:m3 + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:void + PsiKeyword:void('void') + PsiWhiteSpace(' ') + PsiIdentifier:m3('m3') + PsiParameterList:(X Y.Z) + PsiJavaToken:LPARENTH('(') + PsiParameter:Y + PsiModifierList: + + PsiTypeElement:X + PsiJavaCodeReferenceElement:X + PsiIdentifier:X('X') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:Y('Y') + PsiErrorElement:',' expected + PsiJavaToken:DOT('.') + PsiJavaCodeReferenceElement:Z + PsiIdentifier:Z('Z') + PsiReferenceParameterList + + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiField:f1 + PsiModifierList: + + PsiTypeElement:T + PsiJavaCodeReferenceElement:T + PsiIdentifier:T('T') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:f1('f1') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiLambdaExpression:(T this) -> { } + PsiParameterList:(T this) + PsiJavaToken:LPARENTH('(') + PsiReceiverParameter + PsiModifierList: + + PsiTypeElement:T + PsiJavaCodeReferenceElement:T + PsiIdentifier:T('T') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiThisExpression:this + PsiReferenceParameterList + + PsiKeyword:this('this') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiJavaToken:ARROW('->') + PsiWhiteSpace(' ') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiField:f2 + PsiModifierList: + + PsiTypeElement:T + PsiJavaCodeReferenceElement:T + PsiIdentifier:T('T') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:f2('f2') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiLambdaExpression:(T T.this) -> { } + PsiParameterList:(T T.this) + PsiJavaToken:LPARENTH('(') + PsiReceiverParameter + PsiModifierList: + + PsiTypeElement:T + PsiJavaCodeReferenceElement:T + PsiIdentifier:T('T') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiThisExpression:T.this + PsiJavaCodeReferenceElement:T + PsiIdentifier:T('T') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiKeyword:this('this') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiJavaToken:ARROW('->') + PsiWhiteSpace(' ') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java index 4f0ecb3d6e7a..4ed84741e8e8 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java @@ -72,6 +72,15 @@ public class DeclarationParserTest extends JavaParsingTestCase { " }\n} }"); } + public void testReceiver() { + doParserTest( + "{ void m1(C this);" + + " void m2(T T.this);" + + " void m3(X Y.Z);" + + " T f1 = (T this) -> { };" + + " T f2 = (T T.this) -> { }; }"); + } + public void testFieldSimple() { doParserTest("{ int field = 0; }"); } public void testFieldMulti() { doParserTest("{ int field1 = 0, field2; }"); } public void testUnclosedBracket() { doParserTest("{ int field[ }"); } diff --git a/java/java-tests/testSrc/com/intellij/psi/JavaStubBuilderTest.java b/java/java-tests/testSrc/com/intellij/psi/JavaStubBuilderTest.java index f9c585556f24..a11e941d31ec 100644 --- a/java/java-tests/testSrc/com/intellij/psi/JavaStubBuilderTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/JavaStubBuilderTest.java @@ -371,7 +371,7 @@ public class JavaStubBuilderTest extends LightIdeaTestCase { "\n" + "class C<@A T extends @A C> implements @A I<@A T> {\n" + " @TA T<@A T1, @A ? extends @A T2> f;\n" + - " @TA T m(/*@A C this,*/ @TA int p) throws @A E {\n" + + " @TA T m(@A C this, @TA int p) throws @A E {\n" + " o.<@A1 C>m();\n" + " new @A2 C();\n" + " C.@A3 B v = (@A4 C)v.new @A5 C();\n" + From fd0061d2460d5fa29d8b881608f68367fb1f1882 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 2 Jul 2015 15:29:22 +0300 Subject: [PATCH 079/137] java: receiver parameter highlighting --- .../analysis/AnnotationsHighlightUtil.java | 62 ++++++++++++++++++- .../daemon/impl/analysis/HighlightUtil.java | 6 +- .../impl/analysis/HighlightVisitorImpl.java | 18 ++++-- .../codeInsight/AnnotationTargetUtil.java | 55 ++++++++-------- .../tree/java/ParameterListElement.java | 34 +++++----- .../src/messages/JavaErrorMessages.properties | 7 +++ .../UnsupportedFeatures7.java | 18 +----- .../annotations/receiverParameters.java | 47 ++++++++++++++ .../annotations/typeAnnotations.java | 15 ----- .../daemon/AnnotationsHighlightingTest.java | 1 + 10 files changed, 182 insertions(+), 81 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/receiverParameters.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AnnotationsHighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AnnotationsHighlightUtil.java index ad64ccac3f8e..5a3b720dc48e 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AnnotationsHighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AnnotationsHighlightUtil.java @@ -36,6 +36,7 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.ObjectUtils; import com.intellij.util.containers.HashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -663,8 +664,7 @@ public class AnnotationsHighlightUtil { if (methods.length == 1) { PsiType expected = new PsiImmediateClassType((PsiClass)target, PsiSubstitutor.EMPTY).createArrayType(); if (!expected.equals(methods[0].getReturnType())) { - return JavaErrorMessages.message("annotation.container.bad.type", container.getQualifiedName(), JavaHighlightUtil - .formatType(expected)); + return JavaErrorMessages.message("annotation.container.bad.type", container.getQualifiedName(), JavaHighlightUtil.formatType(expected)); } } @@ -698,6 +698,64 @@ public class AnnotationsHighlightUtil { return container; } + @Nullable + static HighlightInfo checkReceiverPlacement(PsiReceiverParameter parameter) { + PsiElement owner = parameter.getParent().getParent(); + if (owner == null) return null; + + if (!(owner instanceof PsiMethod)) { + String text = JavaErrorMessages.message("receiver.wrong.context"); + return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(parameter.getIdentifier()).descriptionAndTooltip(text).create(); + } + + PsiMethod method = (PsiMethod)owner; + if (isStatic(method) || (method).isConstructor() && isStatic(method.getContainingClass())) { + String text = JavaErrorMessages.message("receiver.static.context"); + return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(parameter.getIdentifier()).descriptionAndTooltip(text).create(); + } + + PsiElement leftNeighbour = PsiTreeUtil.skipSiblingsBackward(parameter, PsiWhiteSpace.class); + if (leftNeighbour != null && !PsiUtil.isJavaToken(leftNeighbour, JavaTokenType.LPARENTH)) { + String text = JavaErrorMessages.message("receiver.wrong.position"); + return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(parameter.getIdentifier()).descriptionAndTooltip(text).create(); + } + + return null; + } + + @Nullable + static HighlightInfo checkReceiverType(PsiReceiverParameter parameter) { + PsiElement owner = parameter.getParent().getParent(); + if (!(owner instanceof PsiMethod)) return null; + + PsiMethod method = (PsiMethod)owner; + PsiClass enclosingClass = method.getContainingClass(); + if (method.isConstructor() && enclosingClass != null) { + enclosingClass = enclosingClass.getContainingClass(); + } + + if (enclosingClass != null && !enclosingClass.equals(PsiUtil.resolveClassInType(parameter.getType()))) { + PsiElement range = ObjectUtils.notNull(parameter.getTypeElement(), parameter); + String text = JavaErrorMessages.message("receiver.type.mismatch"); + return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range).descriptionAndTooltip(text).create(); + } + + PsiThisExpression identifier = parameter.getIdentifier(); + if (enclosingClass != null && !enclosingClass.equals(PsiUtil.resolveClassInType(identifier.getType()))) { + String text = JavaErrorMessages.message("receiver.name.mismatch"); + return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(identifier).descriptionAndTooltip(text).create(); + } + + return null; + } + + private static boolean isStatic(PsiModifierListOwner owner) { + if (owner == null) return false; + if (owner instanceof PsiClass && ((PsiClass)owner).getContainingClass() == null) return true; + PsiModifierList modifierList = owner.getModifierList(); + return modifierList != null && modifierList.hasModifierProperty(PsiModifier.STATIC); + } + @Nullable public static RetentionPolicy getRetentionPolicy(@NotNull PsiClass annotation) { PsiModifierList modifierList = annotation.getModifierList(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java index 02e2b1bc58b7..39ffd8c42e59 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java @@ -965,6 +965,9 @@ public class HighlightUtil extends HighlightUtilBase { else if (modifierOwner instanceof PsiLocalVariable || modifierOwner instanceof PsiParameter) { isAllowed = PsiModifier.FINAL.equals(modifier); } + else if (modifierOwner instanceof PsiReceiverParameter) { + isAllowed = false; + } isAllowed &= incompatibles != null; if (!isAllowed) { @@ -2899,7 +2902,8 @@ public class HighlightUtil extends HighlightUtilBase { EXTENSION_METHODS(LanguageLevel.JDK_1_8, "feature.extension.methods"), METHOD_REFERENCES(LanguageLevel.JDK_1_8, "feature.method.references"), LAMBDA_EXPRESSIONS(LanguageLevel.JDK_1_8, "feature.lambda.expressions"), - TYPE_ANNOTATIONS(LanguageLevel.JDK_1_8, "feature.type.annotations"); + TYPE_ANNOTATIONS(LanguageLevel.JDK_1_8, "feature.type.annotations"), + RECEIVERS(LanguageLevel.JDK_1_8, "feature.type.receivers"); private final LanguageLevel level; private final String key; diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index b6110bf5ee92..7012c7603aec 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -1460,12 +1460,10 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh @Override public void visitThisExpression(PsiThisExpression expr) { - myHolder.add(HighlightUtil.checkThisOrSuperExpressionInIllegalContext(expr, expr.getQualifier(), myLanguageLevel)); - if (!myHolder.hasErrorResults()) { - myHolder.add(HighlightUtil.checkMemberReferencedBeforeConstructorCalled(expr, null, myFile)); - } - if (!myHolder.hasErrorResults()) { - visitExpression(expr); + if (!(expr.getParent() instanceof PsiReceiverParameter)) { + myHolder.add(HighlightUtil.checkThisOrSuperExpressionInIllegalContext(expr, expr.getQualifier(), myLanguageLevel)); + if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkMemberReferencedBeforeConstructorCalled(expr, null, myFile)); + if (!myHolder.hasErrorResults()) visitExpression(expr); } } @@ -1580,6 +1578,14 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh } } + @Override + public void visitReceiverParameter(PsiReceiverParameter parameter) { + super.visitReceiverParameter(parameter); + if (!myHolder.hasErrorResults()) myHolder.add(checkFeature(parameter, Feature.RECEIVERS)); + if (!myHolder.hasErrorResults()) myHolder.add(AnnotationsHighlightUtil.checkReceiverPlacement(parameter)); + if (!myHolder.hasErrorResults()) myHolder.add(AnnotationsHighlightUtil.checkReceiverType(parameter)); + } + @Nullable private HighlightInfo checkFeature(@NotNull PsiElement element, @NotNull Feature feature) { return HighlightUtil.checkFeature(element, feature, myLanguageLevel, myFile); diff --git a/java/java-psi-api/src/com/intellij/codeInsight/AnnotationTargetUtil.java b/java/java-psi-api/src/com/intellij/codeInsight/AnnotationTargetUtil.java index c7d9e303aae3..c1a747fb3b03 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/AnnotationTargetUtil.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/AnnotationTargetUtil.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. @@ -17,6 +17,7 @@ package com.intellij.codeInsight; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.*; +import com.intellij.psi.PsiAnnotation.TargetType; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -28,28 +29,27 @@ import java.util.Set; * @author peter */ public class AnnotationTargetUtil { - public static final Set DEFAULT_TARGETS = Collections.unmodifiableSet(ContainerUtil.newHashSet( - PsiAnnotation.TargetType.PACKAGE, PsiAnnotation.TargetType.TYPE, PsiAnnotation.TargetType.ANNOTATION_TYPE, - PsiAnnotation.TargetType.FIELD, PsiAnnotation.TargetType.METHOD, PsiAnnotation.TargetType.CONSTRUCTOR, - PsiAnnotation.TargetType.PARAMETER, PsiAnnotation.TargetType.LOCAL_VARIABLE)); private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.AnnotationUtil"); - private static final PsiAnnotation.TargetType[] PACKAGE_TARGETS = {PsiAnnotation.TargetType.PACKAGE}; - private static final PsiAnnotation.TargetType[] TYPE_USE_TARGETS = {PsiAnnotation.TargetType.TYPE_USE}; - private static final PsiAnnotation.TargetType[] ANNOTATION_TARGETS = {PsiAnnotation.TargetType.ANNOTATION_TYPE, PsiAnnotation.TargetType.TYPE, PsiAnnotation.TargetType.TYPE_USE}; - private static final PsiAnnotation.TargetType[] TYPE_TARGETS = {PsiAnnotation.TargetType.TYPE, PsiAnnotation.TargetType.TYPE_USE}; - private static final PsiAnnotation.TargetType[] TYPE_PARAMETER_TARGETS = { - PsiAnnotation.TargetType.TYPE_PARAMETER, PsiAnnotation.TargetType.TYPE_USE}; - private static final PsiAnnotation.TargetType[] CONSTRUCTOR_TARGETS = {PsiAnnotation.TargetType.CONSTRUCTOR, PsiAnnotation.TargetType.TYPE_USE}; - private static final PsiAnnotation.TargetType[] METHOD_TARGETS = {PsiAnnotation.TargetType.METHOD, PsiAnnotation.TargetType.TYPE_USE}; - private static final PsiAnnotation.TargetType[] FIELD_TARGETS = {PsiAnnotation.TargetType.FIELD, PsiAnnotation.TargetType.TYPE_USE}; - private static final PsiAnnotation.TargetType[] PARAMETER_TARGETS = {PsiAnnotation.TargetType.PARAMETER, PsiAnnotation.TargetType.TYPE_USE}; - private static final PsiAnnotation.TargetType[] LOCAL_VARIABLE_TARGETS ={ - PsiAnnotation.TargetType.LOCAL_VARIABLE, PsiAnnotation.TargetType.TYPE_USE}; + + public static final Set DEFAULT_TARGETS = ContainerUtil.immutableSet( + TargetType.PACKAGE, TargetType.TYPE, TargetType.ANNOTATION_TYPE, TargetType.FIELD, TargetType.METHOD, TargetType.CONSTRUCTOR, + TargetType.PARAMETER, TargetType.LOCAL_VARIABLE); + + private static final TargetType[] PACKAGE_TARGETS = {TargetType.PACKAGE}; + private static final TargetType[] TYPE_USE_TARGETS = {TargetType.TYPE_USE}; + private static final TargetType[] ANNOTATION_TARGETS = {TargetType.ANNOTATION_TYPE, TargetType.TYPE, TargetType.TYPE_USE}; + private static final TargetType[] TYPE_TARGETS = {TargetType.TYPE, TargetType.TYPE_USE}; + private static final TargetType[] TYPE_PARAMETER_TARGETS = {TargetType.TYPE_PARAMETER, TargetType.TYPE_USE}; + private static final TargetType[] CONSTRUCTOR_TARGETS = {TargetType.CONSTRUCTOR, TargetType.TYPE_USE}; + private static final TargetType[] METHOD_TARGETS = {TargetType.METHOD, TargetType.TYPE_USE}; + private static final TargetType[] FIELD_TARGETS = {TargetType.FIELD, TargetType.TYPE_USE}; + private static final TargetType[] PARAMETER_TARGETS = {TargetType.PARAMETER, TargetType.TYPE_USE}; + private static final TargetType[] LOCAL_VARIABLE_TARGETS = {TargetType.LOCAL_VARIABLE, TargetType.TYPE_USE}; @NotNull - public static PsiAnnotation.TargetType[] getTargetsForLocation(@Nullable PsiAnnotationOwner owner) { + public static TargetType[] getTargetsForLocation(@Nullable PsiAnnotationOwner owner) { if (owner == null) { - return PsiAnnotation.TargetType.EMPTY_ARRAY; + return TargetType.EMPTY_ARRAY; } if (owner instanceof PsiType || owner instanceof PsiTypeElement) { @@ -90,24 +90,27 @@ public class AnnotationTargetUtil { if (element instanceof PsiLocalVariable) { return LOCAL_VARIABLE_TARGETS; } + if (element instanceof PsiReceiverParameter) { + return TYPE_USE_TARGETS; + } } - return PsiAnnotation.TargetType.EMPTY_ARRAY; + return TargetType.EMPTY_ARRAY; } @Nullable - public static Set extractRequiredAnnotationTargets(@Nullable PsiAnnotationMemberValue value) { + public static Set extractRequiredAnnotationTargets(@Nullable PsiAnnotationMemberValue value) { if (value instanceof PsiReference) { - PsiAnnotation.TargetType targetType = translateTargetRef((PsiReference)value); + TargetType targetType = translateTargetRef((PsiReference)value); if (targetType != null) { return Collections.singleton(targetType); } } else if (value instanceof PsiArrayInitializerMemberValue) { - Set targets = ContainerUtil.newHashSet(); + Set targets = ContainerUtil.newHashSet(); for (PsiAnnotationMemberValue initializer : ((PsiArrayInitializerMemberValue)value).getInitializers()) { if (initializer instanceof PsiReference) { - PsiAnnotation.TargetType targetType = translateTargetRef((PsiReference)initializer); + TargetType targetType = translateTargetRef((PsiReference)initializer); if (targetType != null) { targets.add(targetType); } @@ -120,12 +123,12 @@ public class AnnotationTargetUtil { } @Nullable - private static PsiAnnotation.TargetType translateTargetRef(@NotNull PsiReference reference) { + private static TargetType translateTargetRef(@NotNull PsiReference reference) { PsiElement field = reference.resolve(); if (field instanceof PsiEnumConstant) { String name = ((PsiEnumConstant)field).getName(); try { - return PsiAnnotation.TargetType.valueOf(name); + return TargetType.valueOf(name); } catch (IllegalArgumentException e) { LOG.warn("Unknown target: " + name); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/ParameterListElement.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/ParameterListElement.java index 20d504d8dbae..cb7fbea9ff4c 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/ParameterListElement.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/ParameterListElement.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. @@ -19,10 +19,7 @@ import com.intellij.lang.ASTNode; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.impl.source.Constants; -import com.intellij.psi.impl.source.tree.ChildRole; -import com.intellij.psi.impl.source.tree.CompositeElement; -import com.intellij.psi.impl.source.tree.JavaSourceUtil; -import com.intellij.psi.impl.source.tree.TreeElement; +import com.intellij.psi.impl.source.tree.*; import com.intellij.psi.tree.ChildRoleBase; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; @@ -32,7 +29,8 @@ import org.jetbrains.annotations.Nullable; public class ParameterListElement extends CompositeElement implements Constants { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.java.ParameterListElement"); - private static final TokenSet PARAMETER_SET = TokenSet.create(PARAMETER); + + private static final TokenSet PARAMETER_SET = TokenSet.create(JavaElementType.PARAMETER, JavaElementType.RECEIVER_PARAMETER); public ParameterListElement() { super(PARAMETER_LIST); @@ -50,8 +48,10 @@ public class ParameterListElement extends CompositeElement implements Constants before = Boolean.FALSE; } } + TreeElement firstAdded = super.addInternal(first, last, anchor, before); - if (first == last && first.getElementType() == PARAMETER) { + + if (first == last && PARAMETER_SET.contains(first.getElementType())) { JavaSourceUtil.addSeparatingComma(this, first, PARAMETER_SET); } @@ -69,9 +69,11 @@ public class ParameterListElement extends CompositeElement implements Constants public void deleteChildInternal(@NotNull ASTNode child) { final TreeElement oldLastNodeInsideParens = getLastNodeInsideParens(); final TreeElement oldFirstNodeInsideParens = getFirstNodeInsideParens(); - if (child.getElementType() == PARAMETER) { + + if (PARAMETER_SET.contains(child.getElementType())) { JavaSourceUtil.deleteSeparatingComma(this, child); } + super.deleteChildInternal(child); // We may want to fix trailing white space processing here - there is a following possible case: @@ -79,10 +81,11 @@ public class ParameterListElement extends CompositeElement implements Constants // *) 'arg2' is to be removed; // We don't want to keep trailing white space then TreeElement newLastNodeInsideParens = getLastNodeInsideParens(); - if (newLastNodeInsideParens != null && newLastNodeInsideParens.getElementType() == WHITE_SPACE) { + if (newLastNodeInsideParens != null && oldLastNodeInsideParens != null && newLastNodeInsideParens.getElementType() == WHITE_SPACE) { if (oldLastNodeInsideParens.getElementType() != WHITE_SPACE) { deleteChildInternal(newLastNodeInsideParens); - } else { + } + else { replaceChild(newLastNodeInsideParens, (ASTNode)oldLastNodeInsideParens.clone()); } } @@ -91,7 +94,8 @@ public class ParameterListElement extends CompositeElement implements Constants if (newFirstNodeInsideParens != null && newFirstNodeInsideParens.getElementType() == WHITE_SPACE) { if (oldFirstNodeInsideParens == null || oldFirstNodeInsideParens.getElementType() != WHITE_SPACE) { deleteChildInternal(newFirstNodeInsideParens); - } else { + } + else { replaceChild(newFirstNodeInsideParens, (ASTNode)oldFirstNodeInsideParens.clone()); } } @@ -126,7 +130,7 @@ public class ParameterListElement extends CompositeElement implements Constants public int getChildRole(ASTNode child) { LOG.assertTrue(child.getTreeParent() == this); IElementType i = child.getElementType(); - if (i == PARAMETER) { + if (PARAMETER_SET.contains((i))) { return ChildRole.PARAMETER; } else if (i == COMMA) { @@ -144,7 +148,7 @@ public class ParameterListElement extends CompositeElement implements Constants } /** - * @return last node before closing right paren if possible; null otherwise + * @return last node before closing right parenthesis if possible; null otherwise */ @Nullable private TreeElement getLastNodeInsideParens() { @@ -152,8 +156,8 @@ public class ParameterListElement extends CompositeElement implements Constants return lastNode.getElementType() == RPARENTH ? lastNode.getTreePrev() : null; } - /** - * @return first node after opening left paren if possible; null otherwise + /** + * @return first node after opening left parenthesis if possible; null otherwise */ @Nullable private TreeElement getFirstNodeInsideParens() { diff --git a/java/java-psi-impl/src/messages/JavaErrorMessages.properties b/java/java-psi-impl/src/messages/JavaErrorMessages.properties index ffee2225b402..eee3b65ce7b1 100644 --- a/java/java-psi-impl/src/messages/JavaErrorMessages.properties +++ b/java/java-psi-impl/src/messages/JavaErrorMessages.properties @@ -30,6 +30,12 @@ annotation.non.repeatable=The declaration of ''{0}'' does not have a valid java. annotation.container.wrong.place=Container annotation ''{0}'' must not be present at the same time as the element it contains annotation.container.not.applicable=Container annotation ''@{0}'' is not applicable to {1} +receiver.wrong.context=Receivers are not allowed outside of method parameter list +receiver.static.context=The receiver cannot be used in a static context +receiver.wrong.position=The receiver should be the first parameter +receiver.type.mismatch=The receiver type does not match the enclosing class type +receiver.name.mismatch=The receiver name does not match the enclosing class type + # These aren't unused. # suppress inspection "UnusedProperty" annotation.target.ANNOTATION_TYPE=annotation type @@ -388,5 +394,6 @@ feature.extension.methods=Extension methods feature.method.references=Method references feature.lambda.expressions=Lambda expressions feature.type.annotations=Type annotations +feature.type.receivers=Receiver parameters insufficient.language.level={0} are not supported at this language level bad.qualifier.in.super.method.reference=Bad type qualifier in default super call: redundant interface {0} is extended by {1} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting6/UnsupportedFeatures7.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting6/UnsupportedFeatures7.java index 168b31bcf55a..19228d9efebc 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting6/UnsupportedFeatures7.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting6/UnsupportedFeatures7.java @@ -1,19 +1,3 @@ -/* - * Copyright 2000-2012 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. - */ - import java.io.*; import java.util.*; @@ -47,6 +31,8 @@ class UnsupportedFeatures { } } + void f(Object this) { } + interface I { default void m() { } static void m() { } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/receiverParameters.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/receiverParameters.java new file mode 100644 index 000000000000..8bebd698486f --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/receiverParameters.java @@ -0,0 +1,47 @@ +import java.lang.annotation.*; + +@interface A { } + +@Target(ElementType.TYPE_USE) +@interface TA { } + +class C { + @Override + public String toString(@TA C this) { return ""; } + + @Override + public boolean equals(@TA C this, @TA Object other) { return false; } + + @interface Anno { String f(Anno this); } + + void m0() { + try (Object this) { } + Runnable r = (C C.this) -> { }; + } + + void m1a(final C this) { } + void m1b(@A C this) { } + + void m2(@TA Object other, @TA C this) { } + + void m3a(@TA Object this) { } + void m3b(@TA int this) { } + + void m4a(C C.this) { } + void m4b(C C.X.this) { } + + static void sm1(@TA Object this) { } + + C(C this) { } + + static class X { + X(X this) { } + } + + class B { + B(C C.this) { } + B(B C.this, int p) { } + B(C B.this, long p) { } + B(C this, float p) { } + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/typeAnnotations.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/typeAnnotations.java index c1fbcaf07cd6..5c000c2366dc 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/typeAnnotations.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/typeAnnotations.java @@ -130,19 +130,4 @@ class Outer { IntFunction f = Outer.@TA This.super::getField; } } - - //todo[r.sh] - /*public String toString(@TA C this) { return ""; } - - public boolean equals(@TA C this, @TA C other) { return false; } - - C(@TA C this, boolean b) { } - - class Outer { - class Middle { - class Inner { - void innerMethod(@TA Outer.@TA Middle.@TA Inner this) { } - } - } - }*/ } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/AnnotationsHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/AnnotationsHighlightingTest.java index 3c1ffcd055ef..e94b6bf2618f 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/AnnotationsHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/AnnotationsHighlightingTest.java @@ -49,6 +49,7 @@ public class AnnotationsHighlightingTest extends LightDaemonAnalyzerTestCase { public void testTypeAnnotations() { doTest8(false); } public void testRepeatable() { doTest8(false); } public void testEnumValues() { doTest8(false); } + public void testReceiverParameters() { doTest8(false); } private void doTest(boolean checkWarnings) { setLanguageLevel(LanguageLevel.JDK_1_7); From 19613ee60e53eda4cf469a3557f59f94fc9bbc0e Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 2 Jul 2015 15:51:44 +0300 Subject: [PATCH 080/137] java: receiver parameter vetoed from rename --- .../refactoring/rename/JavaVetoRenameCondition.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/java/java-impl/src/com/intellij/refactoring/rename/JavaVetoRenameCondition.java b/java/java-impl/src/com/intellij/refactoring/rename/JavaVetoRenameCondition.java index e0baa8787526..2d20a2b0441d 100644 --- a/java/java-impl/src/com/intellij/refactoring/rename/JavaVetoRenameCondition.java +++ b/java/java-impl/src/com/intellij/refactoring/rename/JavaVetoRenameCondition.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. @@ -23,11 +23,16 @@ import com.intellij.psi.util.FileTypeUtils; public class JavaVetoRenameCondition implements Condition { @Override - public boolean value(final PsiElement element) { + public boolean value(PsiElement element) { if (element instanceof LightMethod) { - final PsiClass containingClass = ((LightMethod)element).getContainingClass(); + PsiClass containingClass = ((LightMethod)element).getContainingClass(); if (containingClass != null && containingClass.isEnum()) return true; } + + if (element instanceof PsiReceiverParameter) { + return true; + } + return element instanceof PsiJavaFile && !FileTypeUtils.isInServerPageFile(element) && !JavaProjectRootsUtil.isOutsideJavaSourceRoot((PsiFile)element) && From 0888e688fdac9293eadb39c77f63df9057a82f7b Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 2 Jul 2015 16:06:52 +0300 Subject: [PATCH 081/137] Cleanup (unneeded test parameters) --- .../package-info.java | 0 ...=> packageAnnotationNotInPackageInfo.java} | 0 .../daemon/AnnotationsHighlightingTest.java | 59 +++++++++---------- 3 files changed, 29 insertions(+), 30 deletions(-) rename java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/{invalidPackageAnnotationTarget => }/package-info.java (100%) rename java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/{packageAnnotationNotInPackageInfo/notPackageInfo.java => packageAnnotationNotInPackageInfo.java} (100%) diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/invalidPackageAnnotationTarget/package-info.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/package-info.java similarity index 100% rename from java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/invalidPackageAnnotationTarget/package-info.java rename to java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/package-info.java diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/packageAnnotationNotInPackageInfo/notPackageInfo.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/packageAnnotationNotInPackageInfo.java similarity index 100% rename from java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/packageAnnotationNotInPackageInfo/notPackageInfo.java rename to java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/packageAnnotationNotInPackageInfo.java diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/AnnotationsHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/AnnotationsHighlightingTest.java index e94b6bf2618f..c0cf9a49f3e0 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/AnnotationsHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/AnnotationsHighlightingTest.java @@ -23,41 +23,40 @@ import com.intellij.pom.java.LanguageLevel; public class AnnotationsHighlightingTest extends LightDaemonAnalyzerTestCase { private static final String BASE_PATH = "/codeInsight/daemonCodeAnalyzer/annotations"; - public void testWrongPlace() { doTest(false); } - public void testNotValueNameOmitted() { doTest(false); } - public void testCannotFindMethod() { doTest(false); } - public void testIncompatibleType1() { doTest(false); } - public void testIncompatibleType2() { doTest(false); } - public void testIncompatibleType3() { doTest(false); } - public void testIncompatibleType4() { doTest(false); } - public void testIncompatibleType5() { doTest(false); } - public void testMissingAttribute() { doTest(false); } - public void testDuplicateAnnotation() { doTest(false); } - public void testNonConstantInitializer() { doTest(false); } - public void testInvalidType() { doTest(false); } - public void testInapplicable() { doTest(false); } - public void testDuplicateAttribute() { doTest(false); } - public void testDuplicateTarget() { doTest(false); } - public void testPingPongAnnotationTypesDependencies() { doTest(false); } - public void testClashMethods() { doTest(false); } - public void testDupMethods() { doTest(false); } - public void testPrivateInaccessibleConstant() { doTest(false); } + public void testWrongPlace() { doTest(); } + public void testNotValueNameOmitted() { doTest(); } + public void testCannotFindMethod() { doTest(); } + public void testIncompatibleType1() { doTest(); } + public void testIncompatibleType2() { doTest(); } + public void testIncompatibleType3() { doTest(); } + public void testIncompatibleType4() { doTest(); } + public void testIncompatibleType5() { doTest(); } + public void testMissingAttribute() { doTest(); } + public void testDuplicateAnnotation() { doTest(); } + public void testNonConstantInitializer() { doTest(); } + public void testInvalidType() { doTest(); } + public void testInapplicable() { doTest(); } + public void testDuplicateAttribute() { doTest(); } + public void testDuplicateTarget() { doTest(); } + public void testPingPongAnnotationTypesDependencies() { doTest(); } + public void testClashMethods() { doTest(); } + public void testDupMethods() { doTest(); } + public void testPrivateInaccessibleConstant() { doTest(); } + public void testInvalidPackageAnnotationTarget() { doTest(BASE_PATH + "/package-info.java", false, false); } + public void testPackageAnnotationNotInPackageInfo() { doTest(); } - public void testInvalidPackageAnnotationTarget() { doTest(BASE_PATH + "/" + getTestName(true) + "/package-info.java", false, false); } - public void testPackageAnnotationNotInPackageInfo() { doTest(BASE_PATH + "/" + getTestName(true) + "/notPackageInfo.java", false, false); } + public void testTypeAnnotations() { doTest8(); } + public void testRepeatable() { doTest8(); } + public void testEnumValues() { doTest8(); } + public void testReceiverParameters() { doTest8(); } - public void testTypeAnnotations() { doTest8(false); } - public void testRepeatable() { doTest8(false); } - public void testEnumValues() { doTest8(false); } - public void testReceiverParameters() { doTest8(false); } - - private void doTest(boolean checkWarnings) { + private void doTest() { setLanguageLevel(LanguageLevel.JDK_1_7); - doTest(BASE_PATH + "/" + getTestName(true) + ".java", checkWarnings, false); + doTest(BASE_PATH + "/" + getTestName(true) + ".java", false, false); } - private void doTest8(boolean checkWarnings) { + private void doTest8() { setLanguageLevel(LanguageLevel.JDK_1_8); - doTest(BASE_PATH + "/" + getTestName(true) + ".java", checkWarnings, false); + doTest(BASE_PATH + "/" + getTestName(true) + ".java", false, false); } } From 4e892c58a23aac261c139c9dc9f3a9cb4a2252d5 Mon Sep 17 00:00:00 2001 From: "Vassiliy.Kudryashov" Date: Thu, 2 Jul 2015 17:02:00 +0300 Subject: [PATCH 082/137] IDEA-53898 Make bookmarks preview window (Shift+F11) resizable and remember its size --- .../ide/bookmarks/actions/BookmarksAction.java | 17 +++++++++++++++-- .../ui/popup/util/MasterDetailPopupBuilder.java | 1 + 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/ide/bookmarks/actions/BookmarksAction.java b/platform/lang-impl/src/com/intellij/ide/bookmarks/actions/BookmarksAction.java index 87fcdfa8300c..c3588e65d797 100644 --- a/platform/lang-impl/src/com/intellij/ide/bookmarks/actions/BookmarksAction.java +++ b/platform/lang-impl/src/com/intellij/ide/bookmarks/actions/BookmarksAction.java @@ -27,9 +27,11 @@ import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.PopupChooserBuilder; +import com.intellij.openapi.util.DimensionService; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.openapi.wm.WindowManager; import com.intellij.ui.components.JBList; import com.intellij.ui.popup.util.DetailViewImpl; import com.intellij.ui.popup.util.ItemWrapper; @@ -37,17 +39,22 @@ import com.intellij.ui.popup.util.MasterDetailPopupBuilder; import com.intellij.ui.speedSearch.FilteringListModel; import com.intellij.util.ArrayUtil; import com.intellij.util.Consumer; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.awt.*; import java.awt.event.KeyEvent; import java.util.*; +import java.util.List; /** * @author max */ public class BookmarksAction extends AnAction implements DumbAware, MasterDetailPopupBuilder.Delegate { + @NonNls private static final String DIMENSION_SERVICE_KEY = "bookmarks"; + private JBPopup myPopup; @Override @@ -76,7 +83,7 @@ public class BookmarksAction extends AnAction implements DumbAware, MasterDetail setList(list). setDelegate(this). setDetailView(new DetailViewImpl(project)). - setDimensionServiceKey("bookmarks"). + setDimensionServiceKey(DIMENSION_SERVICE_KEY). setAddDetailViewToEast(true). setActionsGroup(actions). setPopupTuner(new Consumer() { @@ -106,7 +113,13 @@ public class BookmarksAction extends AnAction implements DumbAware, MasterDetail }.registerCustomShortcutSet(CommonShortcuts.getEditSource(), list); editDescriptionAction.setPopup(myPopup); - myPopup.showCenteredInCurrentWindow(project); + final Point location = DimensionService.getInstance().getLocation(DIMENSION_SERVICE_KEY, project); + if (location != null) { + myPopup.showInScreenCoordinates(WindowManager.getInstance().getFrame(project), location); + } + else { + myPopup.showCenteredInCurrentWindow(project); + } list.getEmptyText().setText("No Bookmarks"); list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); diff --git a/platform/lang-impl/src/com/intellij/ui/popup/util/MasterDetailPopupBuilder.java b/platform/lang-impl/src/com/intellij/ui/popup/util/MasterDetailPopupBuilder.java index faa2bbf4ff65..f0d8ac4a0924 100644 --- a/platform/lang-impl/src/com/intellij/ui/popup/util/MasterDetailPopupBuilder.java +++ b/platform/lang-impl/src/com/intellij/ui/popup/util/MasterDetailPopupBuilder.java @@ -244,6 +244,7 @@ public class MasterDetailPopupBuilder implements MasterController { setAutoselectOnMouseMove(false). setMayBeParent(true). setDimensionServiceKey(myDimensionServiceKey). + setUseDimensionServiceForXYLocation(myDimensionServiceKey != null). setSettingButton(toolBar). setSouthComponent(footerPanel). setItemChoosenCallback(itemCallback). From 526af6f1c1206b7f278459757f4b4b22b18069ea Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Thu, 2 Jul 2015 17:08:07 +0300 Subject: [PATCH 083/137] PseudoFunctionStyleMethodInspection: removed transformation of lambda generating function, fixed most of type parameters issues --- .../PseudoLambdaReplaceTemplate.java | 132 +++++++----------- .../lambdaIsntAnonymous/test_after.java | 8 +- .../simpleTransform/test_after.java | 2 +- .../transformLambda/test.java | 2 +- .../transformLambda/test_after.java | 2 +- .../transformMethodCalling/test.java | 8 +- .../transformMethodCalling/test_after.java | 18 +-- .../transformMethodRef/test_after.java | 2 +- ...StaticPseudoFunctionalStyleMethodTest.java | 4 +- 9 files changed, 76 insertions(+), 102 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/PseudoLambdaReplaceTemplate.java b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/PseudoLambdaReplaceTemplate.java index 4c425ed5d9d9..92566655a237 100644 --- a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/PseudoLambdaReplaceTemplate.java +++ b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/PseudoLambdaReplaceTemplate.java @@ -22,6 +22,8 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.codeStyle.SuggestedNameInfo; +import com.intellij.psi.codeStyle.VariableKind; import com.intellij.psi.impl.PsiDiamondTypeUtil; import com.intellij.psi.impl.PsiSubstitutorImpl; import com.intellij.psi.impl.source.PsiClassReferenceType; @@ -116,7 +118,7 @@ class PseudoLambdaReplaceTemplate { } else if (!(returnType instanceof PsiArrayType)) { return null; } - return validate(parameterTypes, returnType, method); + return validate(parameterTypes, returnType, null, method); } @Nullable @@ -136,7 +138,9 @@ class PseudoLambdaReplaceTemplate { if (argumentTypes.length != expectedParameters.length) { return null; } - return validate(argumentTypes, methodReturnType, expression); + final JavaResolveResult result = expression.getMethodExpression().advancedResolve(false); + final PsiSubstitutor methodSubstitutor = result.getSubstitutor(); + return validate(argumentTypes, methodReturnType, methodSubstitutor, expression); } public String getStreamApiMethodName() { @@ -151,7 +155,10 @@ class PseudoLambdaReplaceTemplate { return myAcceptDefaultValue; } - private ValidationInfo validate(final PsiType[] arguments, final PsiType methodReturnType, final PsiElement context) { + private ValidationInfo validate(final PsiType[] arguments, + final PsiType methodReturnType, + final @Nullable PsiSubstitutor methodSubstitutor, + final PsiElement context) { int lambdaPosition = -1; int defaultValuePosition = -1; int iterablePosition = -1; @@ -168,7 +175,7 @@ class PseudoLambdaReplaceTemplate { for (int i = 0; i < arguments.length; i++) { PsiType type = arguments[i]; - if (isFunction(type, methodReturnType, context)) { + if (isFunction(type, methodReturnType, methodSubstitutor, context)) { if (lambdaPosition == -1) { lambdaPosition = i; continue; @@ -207,7 +214,7 @@ class PseudoLambdaReplaceTemplate { return new ValidationInfo(lambdaPosition, iterablePosition, defaultValuePosition); } - private boolean isFunction(PsiType type, PsiType baseMethodReturnType, PsiElement context) { + private boolean isFunction(PsiType type, PsiType baseMethodReturnType, PsiSubstitutor methodSubstitutor, PsiElement context) { if (type instanceof PsiMethodReferenceType) { final PsiMethodReferenceExpression expression = ((PsiMethodReferenceType)type).getExpression(); final PsiMethod resolvedMethod = (PsiMethod)expression.resolve(); @@ -219,20 +226,21 @@ class PseudoLambdaReplaceTemplate { return false; } final PsiType returnType = LambdaUtil.getFunctionalInterfaceReturnType(expression); - return isSuitableLambdaRole(returnType, baseMethodReturnType, context); + return isSuitableLambdaRole(returnType, baseMethodReturnType, methodSubstitutor, context); } else if (type instanceof PsiLambdaExpressionType) { final PsiLambdaExpression expression = ((PsiLambdaExpressionType)type).getExpression(); final PsiType psiType = LambdaUtil.getFunctionalInterfaceReturnType(expression.getFunctionalInterfaceType()); - return isSuitableLambdaRole(psiType, baseMethodReturnType, context); - } else if (isSuitableFunctionalType(type, baseMethodReturnType, context)) { + return isSuitableLambdaRole(psiType, baseMethodReturnType, methodSubstitutor, context); + } else if (isSuitableFunctionalType(type, baseMethodReturnType, methodSubstitutor, context)) { return true; } return isJavaLangClassType(type) && myLambdaRole == LambdaRole.PREDICATE; } - private boolean isSuitableLambdaRole(final PsiType lambdaReturnType, - final PsiType baseMethodReturnType, - final PsiElement context) { + private boolean isSuitableLambdaRole(PsiType lambdaReturnType, + PsiType baseMethodReturnType, + PsiSubstitutor methodSubstitutor, + PsiElement context) { if (lambdaReturnType == null) { return false; } @@ -244,6 +252,9 @@ class PseudoLambdaReplaceTemplate { } else { LOG.assertTrue(myLambdaRole == LambdaRole.FUNCTION); + if (methodSubstitutor != null) { + lambdaReturnType = methodSubstitutor.substitute(lambdaReturnType); + } if (baseMethodReturnType instanceof PsiClassType) { final PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)baseMethodReturnType).resolveGenerics(); final Map substitutionMap = resolveResult.getSubstitutor().getSubstitutionMap(); @@ -274,19 +285,29 @@ class PseudoLambdaReplaceTemplate { return false; } - private boolean isSuitableFunctionalType(final PsiType type, final PsiType baseMethodReturnType, final PsiElement context) { + private boolean isSuitableFunctionalType(final PsiType type, + final PsiType baseMethodReturnType, + final @Nullable PsiSubstitutor methodSubstitutor, + final PsiElement context) { if (type instanceof PsiClassType) { PsiClass targetClass = ((PsiClassType)type).resolve(); if (targetClass != null) { + //TODO fuu if (targetClass instanceof PsiAnonymousClass) { targetClass = ((PsiAnonymousClass)targetClass).getBaseClassType().resolve(); if (targetClass == null) { return false; } + } else { + if (!LambdaUtil.isFunctionalClass(targetClass)) { + return false; + } + return isSuitableLambdaRole(LambdaUtil.getFunctionalInterfaceReturnType(type), baseMethodReturnType, methodSubstitutor, context); } if (!LambdaUtil.isFunctionalClass(targetClass)) { return false; } + //TODO fuu PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(targetClass); final PsiMethod[] methods = ((PsiClassType)type).resolve().findMethodsByName(method.getName(), false); if (methods.length != 1) { @@ -315,7 +336,7 @@ class PseudoLambdaReplaceTemplate { } else { psiType = method.getReturnType(); } - return isSuitableLambdaRole(psiType, baseMethodReturnType, context); + return isSuitableLambdaRole(psiType, baseMethodReturnType, methodSubstitutor, context); } return false; } else { @@ -340,17 +361,19 @@ class PseudoLambdaReplaceTemplate { int lambdaIndex = validationInfo.getLambdaPosition(); final PsiExpression[] expressions = expression.getArgumentList().getExpressions(); + final PsiExpression iterableExpression = expressions[validationInfo.getIterablePosition()]; + final String pipelineHead = createPipelineHeadText(iterableExpression, force); + if (pipelineHead == null) { + return expression; + } + PsiExpression lambdaExpression = expressions[lambdaIndex]; if (!force) { lambdaExpression = convertClassTypeExpression(lambdaExpression); - lambdaExpression = convertToJavaLambda(lambdaExpression, myStreamApiMethodName); + lambdaExpression = convertToJavaLambda(lambdaExpression); } LOG.assertTrue(lambdaExpression != null); - final PsiExpression iterableExpression = expressions[validationInfo.getIterablePosition()]; - final String pipelineHead = createPipelineHeadText(iterableExpression, lambdaExpression, force); - - final String lambdaExpressionText; final String elementText; if (!StreamApiConstants.FAKE_FIND_MATCHED.equals(myStreamApiMethodName)) { @@ -454,10 +477,7 @@ class PseudoLambdaReplaceTemplate { return null; } - private static PsiExpression convertToJavaLambda(PsiExpression expression, String streamApiMethodName) { - if (streamApiMethodName.equals(StreamApiConstants.FAKE_FIND_MATCHED)) { - streamApiMethodName = StreamApiConstants.FILTER; - } + private static PsiExpression convertToJavaLambda(PsiExpression expression) { if (expression instanceof PsiMethodReferenceExpression) { return expression; } @@ -473,72 +493,19 @@ class PseudoLambdaReplaceTemplate { if (!(type instanceof PsiClassType)) { return null; } - final PsiClass lambdaClass = ((PsiClassType)type).resolve(); + final PsiClassType.ClassResolveResult result = ((PsiClassType)type).resolveGenerics(); + final PsiClass lambdaClass = result.getElement(); if (lambdaClass == null) { return null; } - final String methodName = lambdaClass.getMethods()[0].getName(); - if (tryConvertPseudoLambdaToStreamApi(method, resolveStreamApiLambdaClass(expression.getProject(), streamApiMethodName))) { - return expression; - } - else { - return JavaPsiFacade.getElementFactory(expression.getProject()) - .createExpressionFromText(expression.getText() + "::" + methodName, null); - } + final PsiMethod functionalInterfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(lambdaClass); + LOG.assertTrue(functionalInterfaceMethod != null); + final String methodName = functionalInterfaceMethod.getName(); + return JavaPsiFacade.getElementFactory(expression.getProject()).createExpressionFromText(expression.getText() + "::" + methodName, null); } return AnonymousCanBeLambdaInspection.replacePsiElementWithLambda(expression, true); } - @NotNull - private static PsiClass resolveStreamApiLambdaClass(Project project, String streamApiMethodName) { - final PsiClass javaUtilStream = JavaPsiFacade.getInstance(project) - .findClass(StreamApiConstants.JAVA_UTIL_STREAM_STREAM, GlobalSearchScope.notScope(GlobalSearchScope.projectScope(project))); - LOG.assertTrue(javaUtilStream != null); - final PsiMethod[] methods = javaUtilStream.findMethodsByName(streamApiMethodName, false); - LOG.assertTrue(methods.length == 1); - final PsiMethod method = methods[0]; - final PsiParameter[] parameters = method.getParameterList().getParameters(); - LOG.assertTrue(parameters.length == 1); - final PsiType type = parameters[0].getType(); - LOG.assertTrue(type instanceof PsiClassType); - final PsiClass resolved = ((PsiClassType)type).resolve(); - LOG.assertTrue(resolved != null); - return resolved; - } - - private static boolean tryConvertPseudoLambdaToStreamApi(final @NotNull PsiMethod method, final @NotNull PsiClass expectedReturnClass) { - final PsiType currentReturnType = method.getReturnType(); - if (!(currentReturnType instanceof PsiClassType)) { - LOG.error("pseudo-lambda return type must be class " + currentReturnType); - return true; - } - final PsiClass resolvedCurrentReturnType = ((PsiClassType)currentReturnType).resolve(); - if (expectedReturnClass.getManager().areElementsEquivalent(expectedReturnClass, resolvedCurrentReturnType)) { - return true; - } - final PsiCodeBlock body = method.getBody(); - Collection returnStatements = PsiTreeUtil.findChildrenOfType(body, PsiReturnStatement.class); - returnStatements = ContainerUtil.filter(returnStatements, new Condition() { - @Override - public boolean value(PsiReturnStatement statement) { - return PsiTreeUtil.getParentOfType(statement, PsiMethod.class) == method; - } - }); - if (returnStatements.size() != 1) { - return false; - } - final PsiReturnStatement returnStatement = ContainerUtil.getFirstItem(returnStatements); - assert returnStatement != null; - final PsiExpression returnValue = returnStatement.getReturnValue(); - if (returnValue instanceof PsiNewExpression) { - convertNewExpression(method, (PsiNewExpression)returnValue, expectedReturnClass); - return true; - } - else { - return false; - } - } - @NotNull private static PsiExpression convertClassTypeExpression(PsiExpression expression) { final PsiType type = expression.getType(); @@ -552,7 +519,8 @@ class PseudoLambdaReplaceTemplate { return expression; } - private static String createPipelineHeadText(PsiExpression collectionExpression, PsiExpression lambdaExpression, boolean force) { + private static String createPipelineHeadText(@NotNull PsiExpression collectionExpression, + boolean force) { if (collectionExpression instanceof PsiNewExpression) { final PsiDiamondType.DiamondInferenceResult diamondResolveResult = PsiDiamondTypeImpl.resolveInferredTypesNoCheck((PsiNewExpression)collectionExpression, collectionExpression); diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/lambdaIsntAnonymous/test_after.java b/java/java-tests/testData/inspection/lambdaLibsStatic/lambdaIsntAnonymous/test_after.java index 82652edb89d4..46d0c8963f26 100644 --- a/java/java-tests/testData/inspection/lambdaLibsStatic/lambdaIsntAnonymous/test_after.java +++ b/java/java-tests/testData/inspection/lambdaLibsStatic/lambdaIsntAnonymous/test_after.java @@ -6,13 +6,13 @@ import java.util.Collections; class c { void m() { - Collections.emptyList().stream().allMatch(getPredicate(100)); + Collections.emptyList().stream().allMatch(getPredicate(100)::apply); } - public java.util.function.Predicate getPredicate(final int param) { - return new java.util.function.Predicate() { + public Predicate getPredicate(final int param) { + return new Predicate() { @Override - public boolean test(String input) { + public boolean apply(String input) { System.out.println("lambda param " + param); return false; } diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/simpleTransform/test_after.java b/java/java-tests/testData/inspection/lambdaLibsStatic/simpleTransform/test_after.java index 452f94d0f295..a5a23c03188d 100644 --- a/java/java-tests/testData/inspection/lambdaLibsStatic/simpleTransform/test_after.java +++ b/java/java-tests/testData/inspection/lambdaLibsStatic/simpleTransform/test_after.java @@ -8,7 +8,7 @@ import java.util.stream.Collectors; class c { void m() { - Collections.emptyList().stream().map(input -> { + Collections.emptyList().stream().map(input -> { Collectors c; ArrayList l; System.out.println(input); diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/transformLambda/test.java b/java/java-tests/testData/inspection/lambdaLibsStatic/transformLambda/test.java index cc8ef8ab6ddb..a738f169647d 100644 --- a/java/java-tests/testData/inspection/lambdaLibsStatic/transformLambda/test.java +++ b/java/java-tests/testData/inspection/lambdaLibsStatic/transformLambda/test.java @@ -13,7 +13,7 @@ class c { System.out.println(input); //do something int i = 1; - return input; + return input.intern(); } }); } \ No newline at end of file diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/transformLambda/test_after.java b/java/java-tests/testData/inspection/lambdaLibsStatic/transformLambda/test_after.java index bf6d86ece3a8..56aa7a1c44a9 100644 --- a/java/java-tests/testData/inspection/lambdaLibsStatic/transformLambda/test_after.java +++ b/java/java-tests/testData/inspection/lambdaLibsStatic/transformLambda/test_after.java @@ -12,6 +12,6 @@ class c { System.out.println(input); //do something int i = 1; - return input; + return input.intern(); }).collect(Collectors.toList()); } \ No newline at end of file diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodCalling/test.java b/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodCalling/test.java index addd4facbcf6..1f114dcb0fc0 100644 --- a/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodCalling/test.java +++ b/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodCalling/test.java @@ -1,17 +1,21 @@ import com.google.common.collect.Iterables; import com.google.common.base.Function; +import java.lang.Iterable; import java.lang.String; +import java.util.ArrayList; import java.util.Collections; class c { void m() { - Iterables.transform(new ArrayList<>(), getFunction()) + Iterable trnsfrmd = Iterables.transform(new ArrayList<>(), getFunction()); } Function getFunction() { return new Function() { - + String apply(String s) { + return s.toString().toLowerCase().replace('c', 'h'); + } } } } \ No newline at end of file diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodCalling/test_after.java b/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodCalling/test_after.java index 452f94d0f295..52ae999287e4 100644 --- a/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodCalling/test_after.java +++ b/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodCalling/test_after.java @@ -1,6 +1,7 @@ import com.google.common.collect.Iterables; import com.google.common.base.Function; +import java.lang.Iterable; import java.lang.String; import java.util.ArrayList; import java.util.Collections; @@ -8,13 +9,14 @@ import java.util.stream.Collectors; class c { void m() { - Collections.emptyList().stream().map(input -> { - Collectors c; - ArrayList l; - System.out.println(input); - //do something - int i = 1; - return input; - }).collect(Collectors.toList()) + Iterable trnsfrmd = new ArrayList().stream().map(getFunction()::apply).collect(Collectors.toList()); + } + + Function getFunction() { + return new Function() { + String apply(String s) { + return s.toString().toLowerCase().replace('c', 'h'); + } + } } } \ No newline at end of file diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodRef/test_after.java b/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodRef/test_after.java index 452f94d0f295..a5a23c03188d 100644 --- a/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodRef/test_after.java +++ b/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodRef/test_after.java @@ -8,7 +8,7 @@ import java.util.stream.Collectors; class c { void m() { - Collections.emptyList().stream().map(input -> { + Collections.emptyList().stream().map(input -> { Collectors c; ArrayList l; System.out.println(input); diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/StaticPseudoFunctionalStyleMethodTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/StaticPseudoFunctionalStyleMethodTest.java index 098f85b1561f..05d97edea504 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/StaticPseudoFunctionalStyleMethodTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/StaticPseudoFunctionalStyleMethodTest.java @@ -96,11 +96,11 @@ public class StaticPseudoFunctionalStyleMethodTest extends JavaCodeInsightFixtur doTest(); } - public void _testTransformLambda() { + public void testTransformLambda() { doTest(); } - public void _testTransformMethodCalling() { + public void testTransformMethodCalling() { doTest(); } From d8d012ea86c5a0a1e600ed60f4c904cdf060aac3 Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Thu, 2 Jul 2015 16:10:45 +0200 Subject: [PATCH 084/137] FileUtil.toCanonicalPath can now expand symlinks when it's required to build the valid path that contains /../ * typos in JavaDoc fixed --- platform/util/src/com/intellij/openapi/util/io/FileUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/util/src/com/intellij/openapi/util/io/FileUtil.java b/platform/util/src/com/intellij/openapi/util/io/FileUtil.java index e48f1b4d995f..2db6dbc66d78 100644 --- a/platform/util/src/com/intellij/openapi/util/io/FileUtil.java +++ b/platform/util/src/com/intellij/openapi/util/io/FileUtil.java @@ -647,7 +647,7 @@ public class FileUtil extends FileUtilRt { * Please note that this method is symlink-unfriendly (i.e. result of "/path/to/link/../next" most probably will differ from * what {@link java.io.File#getCanonicalPath()} will return) - so use with care.
*
- * If the path may contain synlinks, use use {@link FileUtil#toCanonicalPath(String, boolean)} + * If the path may contain symlinks, use {@link FileUtil#toCanonicalPath(String, boolean)} instead. */ @Contract("null -> null") public static String toCanonicalPath(@Nullable String path) { From 38b3c9ae8c1a565848cc5590938de3e1031ac3ea Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 2 Jul 2015 13:38:58 +0300 Subject: [PATCH 085/137] junit: compare suites by system.identityHashCode to avoid 'same parameter' problem (IDEA-142153) --- .../JUnitTreeByDescriptionHierarchyTest.java | 35 +++++++++++++++++++ .../intellij/junit4/JUnit4TestListener.java | 25 ++++++------- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java b/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java index 10847ff2bc01..e482faf81b42 100644 --- a/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java +++ b/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java @@ -151,6 +151,41 @@ public class JUnitTreeByDescriptionHierarchyTest { "##teamcity[testFinished name='TestA.testName|[1|]']\n" + "##teamcity[testSuiteFinished name='|[1|]']\n"); } + + @Test + public void testParameterizedClassWithSameParameters() throws Exception { + final String className = "a.TestA"; + final Description aTestClassDescription = Description.createSuiteDescription(className); + final ArrayList tests = new ArrayList(); + for (String paramName : new String[]{"[0]", "[0]"}) { + final Description param1 = Description.createSuiteDescription(paramName); + aTestClassDescription.addChild(param1); + final Description testDescription = Description.createTestDescription(className, "testName" + paramName); + tests.add(testDescription); + param1.addChild(testDescription); + } + doTest(aTestClassDescription, tests, + //tree + "##teamcity[suiteTreeStarted name='|[0|]' locationHint='java:suite://a.TestA.|[0|]']\n" + + "##teamcity[suiteTreeNode name='TestA.testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + + "##teamcity[suiteTreeEnded name='|[0|]']\n" + + "##teamcity[suiteTreeStarted name='|[0|]' locationHint='java:suite://a.TestA.|[0|]']\n" + + "##teamcity[suiteTreeNode name='TestA.testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + + "##teamcity[suiteTreeEnded name='|[0|]']\n", + //start + "##teamcity[enteredTheMatrix]\n" + + "##teamcity[rootName name = 'TestA' comment = 'a' location = 'java:suite://a.TestA']\n" + + "##teamcity[testSuiteStarted name='|[0|]']\n" + + "##teamcity[testStarted name='TestA.testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + + "\n" + + "##teamcity[testFinished name='TestA.testName|[0|]']\n" + + "##teamcity[testSuiteFinished name='|[0|]']\n" + + "##teamcity[testSuiteStarted name='|[0|]']\n" + + "##teamcity[testStarted name='TestA.testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + + "\n" + + "##teamcity[testFinished name='TestA.testName|[0|]']\n" + + "##teamcity[testSuiteFinished name='|[0|]']\n"); + } @Test public void test2SuitesWithTheSameTest() throws Exception { diff --git a/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java b/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java index 6d4e5e33fb7e..673f98b88b11 100644 --- a/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java +++ b/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java @@ -80,7 +80,7 @@ public class JUnit4TestListener extends RunListener { public void testRunFinished(Result result) throws Exception { for (int i = myStartedSuites.size() - 1; i>= 0; i--) { - Object parent = myStartedSuites.get(i); + Object parent = JUnit4ReflectionUtil.getClassName((Description)myStartedSuites.get(i)); myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(getShortName((String)parent)) + "\']"); } myStartedSuites.clear(); @@ -92,29 +92,30 @@ public class JUnit4TestListener extends RunListener { final String classFQN = JUnit4ReflectionUtil.getClassName(description); final List parents = (List)myParents.get(description); - List parentsHierarchy = parents != null && !parents.isEmpty() ? (List)parents.remove(0) : Collections.singletonList(classFQN); + List parentsHierarchy = parents != null && !parents.isEmpty() ? (List)parents.remove(0) : Collections.singletonList(description); int idx = 0; - String currentClass; - String currentParent; + Description currentClass; + Description currentParent; while (idx < myStartedSuites.size() && idx < parentsHierarchy.size()) { - currentClass = (String)myStartedSuites.get(idx); - currentParent = (String)parentsHierarchy.get(parentsHierarchy.size() - 1 - idx); - if (!currentClass.equals(currentParent)) break; + currentClass = (Description)myStartedSuites.get(idx); + currentParent = (Description)parentsHierarchy.get(parentsHierarchy.size() - 1 - idx); + if (System.identityHashCode(currentClass) != System.identityHashCode(currentParent)) break; idx++; } for (int i = myStartedSuites.size() - 1; i >= idx; i--) { - currentClass = (String)myStartedSuites.remove(i); - myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(getShortName(currentClass)) + "\']"); + currentClass = (Description)myStartedSuites.remove(i); + myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(getShortName(JUnit4ReflectionUtil.getClassName(currentClass))) + "\']"); } for (int i = idx; i < parentsHierarchy.size(); i++) { - final String fqName = (String) parentsHierarchy.get(parentsHierarchy.size() - 1 - i); + final Description descriptionFromHistory = (Description)parentsHierarchy.get(parentsHierarchy.size() - 1 - i); + final String fqName = JUnit4ReflectionUtil.getClassName(descriptionFromHistory); final String className = getShortName(fqName); if (!className.equals(myRootName)) { myPrintStream.println("##teamcity[testSuiteStarted name=\'" + escapeName(className) + "\'" + (parents == null ? " locationHint=\'java:suite://" + escapeName(fqName) + "\'" : "") + "]"); - myStartedSuites.add(fqName); + myStartedSuites.add(descriptionFromHistory); } } @@ -298,7 +299,7 @@ public class JUnit4TestListener extends RunListener { if (parent != null) { final String parentClassName = JUnit4ReflectionUtil.getClassName(parent); if (!myRootName.equals(parentClassName)) { - pParents.add(0, parentClassName); + pParents.add(0, parent); } } From 6e6dc3dcd24c7342a3d23f52254a861528bec73b Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 2 Jul 2015 14:55:02 +0300 Subject: [PATCH 086/137] junit: hide fully qualified name from parameterized test children --- .../JUnitTreeByDescriptionHierarchyTest.java | 63 ++++++++++--------- .../intellij/junit4/JUnit4TestListener.java | 25 +++++--- 2 files changed, 50 insertions(+), 38 deletions(-) diff --git a/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java b/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java index e482faf81b42..1e49dcfbcdd8 100644 --- a/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java +++ b/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java @@ -48,18 +48,18 @@ public class JUnitTreeByDescriptionHierarchyTest { doTest(root, tests, "##teamcity[suiteTreeStarted name='TestA' locationHint='java:suite://a.TestA']\n" + "##teamcity[suiteTreeStarted name='|[0|]' locationHint='java:suite://a.TestA.|[0|]']\n" + - "##teamcity[suiteTreeNode name='TestA.testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + + "##teamcity[suiteTreeNode name='testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + "##teamcity[suiteTreeEnded name='|[0|]']\n" + "##teamcity[suiteTreeStarted name='|[1|]' locationHint='java:suite://a.TestA.|[1|]']\n" + - "##teamcity[suiteTreeNode name='TestA.testName|[1|]' locationHint='java:test://a.TestA.testName|[1|]']\n" + + "##teamcity[suiteTreeNode name='testName|[1|]' locationHint='java:test://a.TestA.testName|[1|]']\n" + "##teamcity[suiteTreeEnded name='|[1|]']\n" + "##teamcity[suiteTreeEnded name='TestA']\n" + "##teamcity[suiteTreeStarted name='TestB' locationHint='java:suite://a.TestB']\n" + "##teamcity[suiteTreeStarted name='|[0|]' locationHint='java:suite://a.TestB.|[0|]']\n" + - "##teamcity[suiteTreeNode name='TestB.testName|[0|]' locationHint='java:test://a.TestB.testName|[0|]']\n" + + "##teamcity[suiteTreeNode name='testName|[0|]' locationHint='java:test://a.TestB.testName|[0|]']\n" + "##teamcity[suiteTreeEnded name='|[0|]']\n" + "##teamcity[suiteTreeStarted name='|[1|]' locationHint='java:suite://a.TestB.|[1|]']\n" + - "##teamcity[suiteTreeNode name='TestB.testName|[1|]' locationHint='java:test://a.TestB.testName|[1|]']\n" + + "##teamcity[suiteTreeNode name='testName|[1|]' locationHint='java:test://a.TestB.testName|[1|]']\n" + "##teamcity[suiteTreeEnded name='|[1|]']\n" + "##teamcity[suiteTreeEnded name='TestB']\n", @@ -68,26 +68,26 @@ public class JUnitTreeByDescriptionHierarchyTest { "##teamcity[rootName name = 'root' location = 'java:suite://root']\n" + "##teamcity[testSuiteStarted name='TestA']\n" + "##teamcity[testSuiteStarted name='|[0|]']\n" + - "##teamcity[testStarted name='TestA.testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + + "##teamcity[testStarted name='testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + "\n" + - "##teamcity[testFinished name='TestA.testName|[0|]']\n" + + "##teamcity[testFinished name='testName|[0|]']\n" + "##teamcity[testSuiteFinished name='|[0|]']\n" + "##teamcity[testSuiteStarted name='|[1|]']\n" + - "##teamcity[testStarted name='TestA.testName|[1|]' locationHint='java:test://a.TestA.testName|[1|]']\n" + + "##teamcity[testStarted name='testName|[1|]' locationHint='java:test://a.TestA.testName|[1|]']\n" + "\n" + - "##teamcity[testFinished name='TestA.testName|[1|]']\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[testStarted name='TestB.testName|[0|]' locationHint='java:test://a.TestB.testName|[0|]']\n" + + "##teamcity[testStarted name='testName|[0|]' locationHint='java:test://a.TestB.testName|[0|]']\n" + "\n" + - "##teamcity[testFinished name='TestB.testName|[0|]']\n" + + "##teamcity[testFinished name='testName|[0|]']\n" + "##teamcity[testSuiteFinished name='|[0|]']\n" + "##teamcity[testSuiteStarted name='|[1|]']\n" + - "##teamcity[testStarted name='TestB.testName|[1|]' locationHint='java:test://a.TestB.testName|[1|]']\n" + + "##teamcity[testStarted name='testName|[1|]' locationHint='java:test://a.TestB.testName|[1|]']\n" + "\n" + - "##teamcity[testFinished name='TestB.testName|[1|]']\n" + + "##teamcity[testFinished name='testName|[1|]']\n" + "##teamcity[testSuiteFinished name='|[1|]']\n" + "##teamcity[testSuiteFinished name='TestB']\n"); } @@ -120,7 +120,8 @@ public class JUnitTreeByDescriptionHierarchyTest { "##teamcity[testStarted name='MyTest.testMe' locationHint='java:test://b.MyTest.testMe']\n" + "\n" + "##teamcity[testFinished name='MyTest.testMe']\n" + - "##teamcity[testSuiteFinished name='MyTest']\n"); + "##teamcity[testSuiteFinished nam" + + "e='MyTest']\n"); } @Test @@ -132,23 +133,23 @@ public class JUnitTreeByDescriptionHierarchyTest { doTest(aTestClassDescription, tests, //tree "##teamcity[suiteTreeStarted name='|[0|]' locationHint='java:suite://a.TestA.|[0|]']\n" + - "##teamcity[suiteTreeNode name='TestA.testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + + "##teamcity[suiteTreeNode name='testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + "##teamcity[suiteTreeEnded name='|[0|]']\n" + "##teamcity[suiteTreeStarted name='|[1|]' locationHint='java:suite://a.TestA.|[1|]']\n" + - "##teamcity[suiteTreeNode name='TestA.testName|[1|]' locationHint='java:test://a.TestA.testName|[1|]']\n" + + "##teamcity[suiteTreeNode name='testName|[1|]' locationHint='java:test://a.TestA.testName|[1|]']\n" + "##teamcity[suiteTreeEnded name='|[1|]']\n", //start "##teamcity[enteredTheMatrix]\n" + "##teamcity[rootName name = 'TestA' comment = 'a' location = 'java:suite://a.TestA']\n" + "##teamcity[testSuiteStarted name='|[0|]']\n" + - "##teamcity[testStarted name='TestA.testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + + "##teamcity[testStarted name='testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + "\n" + - "##teamcity[testFinished name='TestA.testName|[0|]']\n" + + "##teamcity[testFinished name='testName|[0|]']\n" + "##teamcity[testSuiteFinished name='|[0|]']\n" + "##teamcity[testSuiteStarted name='|[1|]']\n" + - "##teamcity[testStarted name='TestA.testName|[1|]' locationHint='java:test://a.TestA.testName|[1|]']\n" + + "##teamcity[testStarted name='testName|[1|]' locationHint='java:test://a.TestA.testName|[1|]']\n" + "\n" + - "##teamcity[testFinished name='TestA.testName|[1|]']\n" + + "##teamcity[testFinished name='testName|[1|]']\n" + "##teamcity[testSuiteFinished name='|[1|]']\n"); } @@ -167,23 +168,23 @@ public class JUnitTreeByDescriptionHierarchyTest { doTest(aTestClassDescription, tests, //tree "##teamcity[suiteTreeStarted name='|[0|]' locationHint='java:suite://a.TestA.|[0|]']\n" + - "##teamcity[suiteTreeNode name='TestA.testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + + "##teamcity[suiteTreeNode name='testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + "##teamcity[suiteTreeEnded name='|[0|]']\n" + "##teamcity[suiteTreeStarted name='|[0|]' locationHint='java:suite://a.TestA.|[0|]']\n" + - "##teamcity[suiteTreeNode name='TestA.testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + + "##teamcity[suiteTreeNode name='testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + "##teamcity[suiteTreeEnded name='|[0|]']\n", //start "##teamcity[enteredTheMatrix]\n" + "##teamcity[rootName name = 'TestA' comment = 'a' location = 'java:suite://a.TestA']\n" + "##teamcity[testSuiteStarted name='|[0|]']\n" + - "##teamcity[testStarted name='TestA.testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + + "##teamcity[testStarted name='testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + "\n" + - "##teamcity[testFinished name='TestA.testName|[0|]']\n" + + "##teamcity[testFinished name='testName|[0|]']\n" + "##teamcity[testSuiteFinished name='|[0|]']\n" + "##teamcity[testSuiteStarted name='|[0|]']\n" + - "##teamcity[testStarted name='TestA.testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + + "##teamcity[testStarted name='testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + "\n" + - "##teamcity[testFinished name='TestA.testName|[0|]']\n" + + "##teamcity[testFinished name='testName|[0|]']\n" + "##teamcity[testSuiteFinished name='|[0|]']\n"); } @@ -401,10 +402,10 @@ public class JUnitTreeByDescriptionHierarchyTest { doTest(root, tests, "##teamcity[suiteTreeStarted name='ATest' locationHint='java:suite://ATest']\n" + "##teamcity[suiteTreeStarted name='|[0|]' locationHint='java:suite://ATest.|[0|]']\n" + - "##teamcity[suiteTreeNode name='ATest.testName|[0|]' locationHint='java:test://ATest.testName|[0|]']\n" + + "##teamcity[suiteTreeNode name='testName|[0|]' locationHint='java:test://ATest.testName|[0|]']\n" + "##teamcity[suiteTreeEnded name='|[0|]']\n" + "##teamcity[suiteTreeStarted name='|[1|]' locationHint='java:suite://ATest.|[1|]']\n" + - "##teamcity[suiteTreeNode name='ATest.testName|[1|]' locationHint='java:test://ATest.testName|[1|]']\n" + + "##teamcity[suiteTreeNode name='testName|[1|]' locationHint='java:test://ATest.testName|[1|]']\n" + "##teamcity[suiteTreeEnded name='|[1|]']\n" + "##teamcity[suiteTreeEnded name='ATest']\n" + "##teamcity[suiteTreeStarted name='suite' locationHint='java:suite://suite']\n" + @@ -418,14 +419,14 @@ public class JUnitTreeByDescriptionHierarchyTest { "##teamcity[rootName name = 'root' location = 'java:suite://root']\n" + "##teamcity[testSuiteStarted name='ATest']\n" + "##teamcity[testSuiteStarted name='|[0|]']\n" + - "##teamcity[testStarted name='ATest.testName|[0|]' locationHint='java:test://ATest.testName|[0|]']\n" + + "##teamcity[testStarted name='testName|[0|]' locationHint='java:test://ATest.testName|[0|]']\n" + "\n" + - "##teamcity[testFinished name='ATest.testName|[0|]']\n" + + "##teamcity[testFinished name='testName|[0|]']\n" + "##teamcity[testSuiteFinished name='|[0|]']\n" + "##teamcity[testSuiteStarted name='|[1|]']\n" + - "##teamcity[testStarted name='ATest.testName|[1|]' locationHint='java:test://ATest.testName|[1|]']\n" + + "##teamcity[testStarted name='testName|[1|]' locationHint='java:test://ATest.testName|[1|]']\n" + "\n" + - "##teamcity[testFinished name='ATest.testName|[1|]']\n" + + "##teamcity[testFinished name='testName|[1|]']\n" + "##teamcity[testSuiteFinished name='|[1|]']\n" + "##teamcity[testSuiteFinished name='ATest']\n" + "##teamcity[testSuiteStarted name='suite']\n" + diff --git a/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java b/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java index 673f98b88b11..5beb81ee07db 100644 --- a/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java +++ b/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java @@ -43,6 +43,7 @@ public class JUnit4TestListener extends RunListener { private List myStartedSuites = new ArrayList(); private Map myParents = new HashMap(); + private Map myMethodNames = new HashMap(); private final PrintStream myPrintStream; private String myRootName; private long myCurrentTestStart; @@ -87,12 +88,14 @@ public class JUnit4TestListener extends RunListener { } public void testStarted(Description description) throws Exception { - final String methodName = getFullMethodName(description); - if (methodName == null) return; final String classFQN = JUnit4ReflectionUtil.getClassName(description); final List parents = (List)myParents.get(description); List parentsHierarchy = parents != null && !parents.isEmpty() ? (List)parents.remove(0) : Collections.singletonList(description); + + final String methodName = getFullMethodName(description, parentsHierarchy.isEmpty() ? null + : (Description)parentsHierarchy.get(parentsHierarchy.size() - 1)); + if (methodName == null) return; int idx = 0; Description currentClass; @@ -197,10 +200,18 @@ public class JUnit4TestListener extends RunListener { catch (Exception ignore) {} } - private static String getFullMethodName(Description description) { - final String methodName = JUnit4ReflectionUtil.getMethodName(description); - if (methodName != null) { - return getShortName(JUnit4ReflectionUtil.getClassName(description)) + "." + methodName; + private String getFullMethodName(Description description) { + return getFullMethodName(description, null); + } + + private String getFullMethodName(Description description, Description parent) { + String methodName = (String)myMethodNames.get(description); + if (methodName == null) { + methodName = JUnit4ReflectionUtil.getMethodName(description); + if (methodName != null && (parent == null || !isParameter(parent))) { + methodName = getShortName(JUnit4ReflectionUtil.getClassName(description)) + "." + methodName; + } + myMethodNames.put(description, methodName); } return methodName; } @@ -305,7 +316,7 @@ public class JUnit4TestListener extends RunListener { String className = JUnit4ReflectionUtil.getClassName(description); if (description.getChildren().isEmpty()) { - final String methodName = getFullMethodName((Description)description); + final String methodName = getFullMethodName((Description)description, parent); if (methodName != null) { if (parent != null) { List parents = (List)myParents.get(description); From 063f232ce2564e0e7f60707a1a6618e837eb5ad0 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 2 Jul 2015 14:59:14 +0300 Subject: [PATCH 087/137] testng: provide test count on suite started --- .../src/org/testng/IDEATestNGRemoteListener.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/plugins/testng_rt/src/org/testng/IDEATestNGRemoteListener.java b/plugins/testng_rt/src/org/testng/IDEATestNGRemoteListener.java index 2a1e709cf4a4..1e4926526b18 100644 --- a/plugins/testng_rt/src/org/testng/IDEATestNGRemoteListener.java +++ b/plugins/testng_rt/src/org/testng/IDEATestNGRemoteListener.java @@ -31,6 +31,14 @@ public class IDEATestNGRemoteListener implements ISuiteListener, IResultListener public synchronized void onStart(final ISuite suite) { myPrintStream.println("##teamcity[enteredTheMatrix]"); if (suite != null) { + final List allMethods = suite.getAllMethods(); + if (allMethods != null) { + int count = 0; + for (ITestNGMethod method : allMethods) { + if (method.isTest()) count++; + } + myPrintStream.println("##teamcity[testCount count = \'" + count + "\']"); + } myPrintStream.println("##teamcity[rootName name = '" + suite.getName() + "' location = 'file://" + suite.getXmlSuite().getFileName() + "']"); } } @@ -148,9 +156,6 @@ public class IDEATestNGRemoteListener implements ISuiteListener, IResultListener } private void onTestStart(ExposedTestResult result, String paramString, Integer invocationCount, boolean config) { - if (!config) { - myPrintStream.println("##teamcity[testCount count=\'1\']"); - } myParamsMap.put(result, paramString); onSuiteStart(result.getTestHierarchy(), result, true); final String className = result.getClassName(); From 0e0de35da820304263b53a0416e66bd926b01cb3 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 2 Jul 2015 15:07:05 +0300 Subject: [PATCH 088/137] junit: restore diff comparison for nested comparison failure exception (IDEA-142192) --- .../junit_rt/src/com/intellij/junit4/JUnit4TestListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java b/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java index 5beb81ee07db..0584bffdce88 100644 --- a/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java +++ b/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java @@ -274,7 +274,7 @@ public class JUnit4TestListener extends RunListener { try { final Throwable cause = assertion.getCause(); if (isComparisonFailure(cause)) { - return ComparisonFailureData.create(assertion); + return ComparisonFailureData.create(cause); } } catch (Throwable ignore) { From 4684772197ca13b910ee918000a3ce1a8eb198f8 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 2 Jul 2015 15:50:00 +0300 Subject: [PATCH 089/137] extract method: include method prams when extracting from lambda body (IDEA-142163) --- .../psi/controlFlow/LocalsControlFlowPolicy.java | 3 ++- .../FromLambdaBodyToToplevelInsideCodeBlock.java | 9 +++++++++ ...omLambdaBodyToToplevelInsideCodeBlock_after.java | 13 +++++++++++++ .../com/intellij/refactoring/ExtractMethodTest.java | 4 ++++ 4 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 java/java-tests/testData/refactoring/extractMethod/FromLambdaBodyToToplevelInsideCodeBlock.java create mode 100644 java/java-tests/testData/refactoring/extractMethod/FromLambdaBodyToToplevelInsideCodeBlock_after.java diff --git a/java/java-psi-impl/src/com/intellij/psi/controlFlow/LocalsControlFlowPolicy.java b/java/java-psi-impl/src/com/intellij/psi/controlFlow/LocalsControlFlowPolicy.java index 9e088b82b055..d3cf1a559070 100644 --- a/java/java-psi-impl/src/com/intellij/psi/controlFlow/LocalsControlFlowPolicy.java +++ b/java/java-psi-impl/src/com/intellij/psi/controlFlow/LocalsControlFlowPolicy.java @@ -56,7 +56,8 @@ public class LocalsControlFlowPolicy implements ControlFlowPolicy { if (codeFragment == null) return null; if (myCodeFragment.getContainingFile() == codeFragment.getContainingFile() && // in order for jsp includes to work !myCodeFragment.equals(codeFragment) && - !(myCodeFragment.getParent() instanceof PsiLambdaExpression && codeFragment.equals(PsiTreeUtil.getParentOfType(myCodeFragment, PsiCodeBlock.class)))) { + !(myCodeFragment.getParent() instanceof PsiLambdaExpression && + PsiTreeUtil.isAncestor(PsiTreeUtil.getParentOfType(myCodeFragment, PsiClass.class), codeFragment, false))) { return null; } return (PsiVariable)refElement; diff --git a/java/java-tests/testData/refactoring/extractMethod/FromLambdaBodyToToplevelInsideCodeBlock.java b/java/java-tests/testData/refactoring/extractMethod/FromLambdaBodyToToplevelInsideCodeBlock.java new file mode 100644 index 000000000000..8fd33244505f --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/FromLambdaBodyToToplevelInsideCodeBlock.java @@ -0,0 +1,9 @@ +class Test { + void foo(Object o) { + try { + Runnable r = () -> { + System.out.println(o); + }; + } catch (Throwable e) {} + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethod/FromLambdaBodyToToplevelInsideCodeBlock_after.java b/java/java-tests/testData/refactoring/extractMethod/FromLambdaBodyToToplevelInsideCodeBlock_after.java new file mode 100644 index 000000000000..bd272305fece --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/FromLambdaBodyToToplevelInsideCodeBlock_after.java @@ -0,0 +1,13 @@ +class Test { + void foo(Object o) { + try { + Runnable r = () -> { + newMethod(o); + }; + } catch (Throwable e) {} + } + + private void newMethod(Object o) { + System.out.println(o); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodTest.java b/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodTest.java index 6a232ae4b830..47a038460eb7 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodTest.java @@ -583,6 +583,10 @@ public class ExtractMethodTest extends LightCodeInsightTestCase { public void testFromLambdaBodyToAnonymous() throws Exception { doTest(); } + + public void testFromLambdaBodyToToplevelInsideCodeBlock() throws Exception { + doTest(); + } public void testFromLambdaBodyWithReturn() throws Exception { doTest(); From 1b9455cf11eee680a431ccfb247c5e4d8a5d116f Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Thu, 2 Jul 2015 16:22:20 +0200 Subject: [PATCH 090/137] Ref.deref convenience method --- platform/util/src/com/intellij/openapi/util/Ref.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/platform/util/src/com/intellij/openapi/util/Ref.java b/platform/util/src/com/intellij/openapi/util/Ref.java index 0119c5626bf7..a282abae3163 100644 --- a/platform/util/src/com/intellij/openapi/util/Ref.java +++ b/platform/util/src/com/intellij/openapi/util/Ref.java @@ -59,6 +59,11 @@ public class Ref { return new Ref(value); } + @Nullable + public static T deref(@Nullable Ref ref) { + return ref == null ? null : ref.get(); + } + @Override public String toString() { return String.valueOf(myValue); From 05b5e748eb48afb7be65469377ce6fb78874ceea Mon Sep 17 00:00:00 2001 From: irengrig Date: Thu, 2 Jul 2015 16:30:35 +0200 Subject: [PATCH 091/137] node.js: have caching service for keeping/recalculating known node.js interpreters and their versions use this component in node.js interpreter field; show version there todo: how to show version for selected path in this component? --- .../platform-impl/src/com/intellij/util/ui/SwingHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/util/ui/SwingHelper.java b/platform/platform-impl/src/com/intellij/util/ui/SwingHelper.java index 86c119bf6739..9b0560ec0c4d 100644 --- a/platform/platform-impl/src/com/intellij/util/ui/SwingHelper.java +++ b/platform/platform-impl/src/com/intellij/util/ui/SwingHelper.java @@ -359,7 +359,7 @@ public class SwingHelper { TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT); } - private static void doInstall(@Nullable Project project, + public static void doInstall(@Nullable Project project, @NotNull ComponentWithBrowseButton componentWithBrowseButton, @NotNull JTextField textField, @NotNull String browseDialogTitle, From ac05b12d263fffb0557625db6108c345d5524996 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 2 Jul 2015 17:35:35 +0300 Subject: [PATCH 092/137] Resolve members of all imported modules in both __init__.py and submodules (PY-14454, PY-14615) We used to resolve members __init__.py of imported packages using only the __init__.py itself, i.e. without considering their submodules. Now we do that and we also don't filter unimported submodules because we cannot detect them reliably. They might be imported in other modules via some import chain. --- .../psi/types/PyImportedModuleType.java | 23 ++++--------------- .../ImplicitlyImportedSubModule/a.py | 5 ++++ .../pkg1/__init__.py | 1 + .../ImplicitlyImportedSubModule/pkg1/m1.py | 1 + .../ImplicitlyImportedSubModule/pkg1/m2.py | 1 + .../main.py | 5 ---- .../nspkg1/m2.py | 0 .../nspkg1/m3.py | 0 .../nspkg1/nssubpkg1/m1.py | 0 .../unusedSubmodule/p1/__init__.py | 0 .../unusedImport/unusedSubmodule/p1/m1.py | 1 - .../unusedImport/unusedSubmodule/p1/m2.py | 1 - .../unusedImport/unusedSubmodule/test1.py | 3 --- .../FromImportSubModuleDunderAll.py | 4 ++++ .../pkg1/__init__.py | 1 + .../fromImportSubModuleDunderAll/pkg1/m1.py | 2 ++ .../ImportSubModuleDunderAll.py | 6 ++--- .../python/PyMultiFileResolveTest.java | 6 +++++ ...Py3UnresolvedReferencesInspectionTest.java | 5 ---- .../PyUnresolvedReferencesInspectionTest.java | 5 ++++ .../inspections/PyUnusedImportTest.java | 5 ---- 21 files changed, 33 insertions(+), 42 deletions(-) create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/ImplicitlyImportedSubModule/a.py create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/ImplicitlyImportedSubModule/pkg1/__init__.py create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/ImplicitlyImportedSubModule/pkg1/m1.py create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/ImplicitlyImportedSubModule/pkg1/m2.py delete mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/main.py delete mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/m2.py delete mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/m3.py delete mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/nssubpkg1/m1.py delete mode 100644 python/testData/inspections/unusedImport/unusedSubmodule/p1/__init__.py delete mode 100644 python/testData/inspections/unusedImport/unusedSubmodule/p1/m1.py delete mode 100644 python/testData/inspections/unusedImport/unusedSubmodule/p1/m2.py delete mode 100644 python/testData/inspections/unusedImport/unusedSubmodule/test1.py create mode 100644 python/testData/resolve/multiFile/fromImportSubModuleDunderAll/FromImportSubModuleDunderAll.py create mode 100644 python/testData/resolve/multiFile/fromImportSubModuleDunderAll/pkg1/__init__.py create mode 100644 python/testData/resolve/multiFile/fromImportSubModuleDunderAll/pkg1/m1.py diff --git a/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java b/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java index 01bcf032bfa2..19f247d4a4bf 100644 --- a/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java +++ b/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java @@ -15,14 +15,12 @@ */ package com.jetbrains.python.psi.types; -import com.google.common.collect.Sets; import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.openapi.util.Condition; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; -import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.psi.AccessDirection; import com.jetbrains.python.psi.PyExpression; import com.jetbrains.python.psi.PyFile; @@ -38,7 +36,6 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Set; /** * @author yole @@ -57,21 +54,9 @@ public class PyImportedModuleType implements PyType { @NotNull AccessDirection direction, @NotNull PyResolveContext resolveContext) { final PsiElement resolved = myImportedModule.resolve(); - if (resolved instanceof PyFile) { - final PyFile file = (PyFile)resolved; - return new PyModuleType(file, myImportedModule).resolveMember(name, location, direction, resolveContext); - } - else if (resolved instanceof PsiDirectory) { - List elements = Collections.singletonList(ResolveImportUtil.resolveChild(resolved, name, null, true, true)); - if (location != null && ResolveImportUtil.getPointInImport(location) == PointInImport.NONE) { - final Set imported = Sets.newHashSet(PyModuleType.collectImportedSubmodules((PsiDirectory)resolved, location)); - elements = ContainerUtil.filter(elements, new Condition() { - @Override - public boolean value(PsiElement element) { - return imported.contains(element); - } - }); - } + if (resolved != null) { + final PsiFile containingFile = location != null ? location.getContainingFile() : null; + List elements = Collections.singletonList(ResolveImportUtil.resolveChild(resolved, name, containingFile, false, true)); return ResolveImportUtil.rateResults(elements); } return null; diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/ImplicitlyImportedSubModule/a.py b/python/testData/inspections/PyUnresolvedReferencesInspection/ImplicitlyImportedSubModule/a.py new file mode 100644 index 000000000000..6de874dee44a --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/ImplicitlyImportedSubModule/a.py @@ -0,0 +1,5 @@ +import pkg1.m1 + +print(pkg1.m2) +print(pkg1.nonexistent) + diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/ImplicitlyImportedSubModule/pkg1/__init__.py b/python/testData/inspections/PyUnresolvedReferencesInspection/ImplicitlyImportedSubModule/pkg1/__init__.py new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/ImplicitlyImportedSubModule/pkg1/__init__.py @@ -0,0 +1 @@ + diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/ImplicitlyImportedSubModule/pkg1/m1.py b/python/testData/inspections/PyUnresolvedReferencesInspection/ImplicitlyImportedSubModule/pkg1/m1.py new file mode 100644 index 000000000000..123f7cbbfd9d --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/ImplicitlyImportedSubModule/pkg1/m1.py @@ -0,0 +1 @@ +import pkg1.m2 diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/ImplicitlyImportedSubModule/pkg1/m2.py b/python/testData/inspections/PyUnresolvedReferencesInspection/ImplicitlyImportedSubModule/pkg1/m2.py new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/ImplicitlyImportedSubModule/pkg1/m2.py @@ -0,0 +1 @@ + diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/main.py b/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/main.py deleted file mode 100644 index 54e2273f4550..000000000000 --- a/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/main.py +++ /dev/null @@ -1,5 +0,0 @@ -import nspkg1.m2 - -print(nspkg1.m2) -print(nspkg1.m3) -print(nspkg1.nssubpkg1) diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/m2.py b/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/m2.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/m3.py b/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/m3.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/nssubpkg1/m1.py b/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/nssubpkg1/m1.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/python/testData/inspections/unusedImport/unusedSubmodule/p1/__init__.py b/python/testData/inspections/unusedImport/unusedSubmodule/p1/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/python/testData/inspections/unusedImport/unusedSubmodule/p1/m1.py b/python/testData/inspections/unusedImport/unusedSubmodule/p1/m1.py deleted file mode 100644 index a43e3eb876c1..000000000000 --- a/python/testData/inspections/unusedImport/unusedSubmodule/p1/m1.py +++ /dev/null @@ -1 +0,0 @@ -a1 = 42 \ No newline at end of file diff --git a/python/testData/inspections/unusedImport/unusedSubmodule/p1/m2.py b/python/testData/inspections/unusedImport/unusedSubmodule/p1/m2.py deleted file mode 100644 index 97f0eb4ade34..000000000000 --- a/python/testData/inspections/unusedImport/unusedSubmodule/p1/m2.py +++ /dev/null @@ -1 +0,0 @@ -a2 = -1 \ No newline at end of file diff --git a/python/testData/inspections/unusedImport/unusedSubmodule/test1.py b/python/testData/inspections/unusedImport/unusedSubmodule/test1.py deleted file mode 100644 index 40e9e79d3ac1..000000000000 --- a/python/testData/inspections/unusedImport/unusedSubmodule/test1.py +++ /dev/null @@ -1,3 +0,0 @@ -import p1.m1 - -print(p1.m2.a2) diff --git a/python/testData/resolve/multiFile/fromImportSubModuleDunderAll/FromImportSubModuleDunderAll.py b/python/testData/resolve/multiFile/fromImportSubModuleDunderAll/FromImportSubModuleDunderAll.py new file mode 100644 index 000000000000..e5ee56c5fc66 --- /dev/null +++ b/python/testData/resolve/multiFile/fromImportSubModuleDunderAll/FromImportSubModuleDunderAll.py @@ -0,0 +1,4 @@ +from pkg1 import m1 + +print(m1) +# diff --git a/python/testData/resolve/multiFile/fromImportSubModuleDunderAll/pkg1/__init__.py b/python/testData/resolve/multiFile/fromImportSubModuleDunderAll/pkg1/__init__.py new file mode 100644 index 000000000000..a6d9575178da --- /dev/null +++ b/python/testData/resolve/multiFile/fromImportSubModuleDunderAll/pkg1/__init__.py @@ -0,0 +1 @@ +__all__ = ['m1'] diff --git a/python/testData/resolve/multiFile/fromImportSubModuleDunderAll/pkg1/m1.py b/python/testData/resolve/multiFile/fromImportSubModuleDunderAll/pkg1/m1.py new file mode 100644 index 000000000000..9332a2735b6a --- /dev/null +++ b/python/testData/resolve/multiFile/fromImportSubModuleDunderAll/pkg1/m1.py @@ -0,0 +1,2 @@ +def foo(): + pass diff --git a/python/testData/resolve/multiFile/importSubModuleDunderAll/ImportSubModuleDunderAll.py b/python/testData/resolve/multiFile/importSubModuleDunderAll/ImportSubModuleDunderAll.py index e5ee56c5fc66..7bba9e409ba0 100644 --- a/python/testData/resolve/multiFile/importSubModuleDunderAll/ImportSubModuleDunderAll.py +++ b/python/testData/resolve/multiFile/importSubModuleDunderAll/ImportSubModuleDunderAll.py @@ -1,4 +1,4 @@ -from pkg1 import m1 +import pkg1.m1 -print(m1) -# +print(pkg1.m1) +# diff --git a/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java b/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java index fd5ee281e4a8..fdec830dbaef 100644 --- a/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java +++ b/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java @@ -407,7 +407,13 @@ public class PyMultiFileResolveTest extends PyMultiFileResolveTestCase { assertResolvesTo(PyFile.class, "z.py"); } + // PY-11454 public void testImportSubModuleDunderAll() { assertResolvesTo(PyFile.class, "m1.py"); } + + // PY-11454 + public void testFromImportSubModuleDunderAll() { + assertResolvesTo(PyFile.class, "m1.py"); + } } \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java index 1a29300f947d..9cdac1dd034b 100644 --- a/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java @@ -112,11 +112,6 @@ public class Py3UnresolvedReferencesInspectionTest extends PyTestCase { doTest(); } - // PY-14385 - public void testNotImportedSubmodulesOfNamespacePackage() { - doMultiFileTest("main.py"); - } - // PY-15017 public void testClassLevelReferenceInMethodAnnotation() { doTest(); diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java index 1499084c4def..6e0c1b333f7a 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java @@ -510,6 +510,11 @@ public class PyUnresolvedReferencesInspectionTest extends PyInspectionTestCase { doTest(); } + // PY-14615 + public void testImplicitlyImportedSubModule() { + doMultiFileTest(); + } + @NotNull @Override protected Class getInspectionClass() { diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnusedImportTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnusedImportTest.java index 018d75e8fbde..1ce20f386adf 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnusedImportTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnusedImportTest.java @@ -38,11 +38,6 @@ public class PyUnusedImportTest extends PyTestCase { doTest("test1.py"); } - // PY-5621 - public void testUnusedSubmodule() { - doTest("test1.py"); - } - // PY-6380 public void testUnusedAfterStarImport() { doTest("test1.py"); From 50d764047b9e2a0116af0b73e34f450acdb32cc0 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 2 Jul 2015 17:54:57 +0300 Subject: [PATCH 093/137] tests: bomb planted --- .../com/intellij/openapi/vfs/local/FileWatcherTest.java | 6 ++++++ 1 file changed, 6 insertions(+) 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 9bcf49976616..ed550957e40d 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 @@ -715,6 +715,12 @@ public class FileWatcherTest extends PlatformTestCase { } public void testDisplacementByIsomorphicTree() throws Exception { + if (SystemInfo.isMac) { + assertTrue("Kaboom", new GregorianCalendar(2015, Calendar.SEPTEMBER, 1).getTimeInMillis() > new Date().getTime()); + System.out.println("** skipped"); + return; + } + File top = createTestDir("top"); File up = createTestDir(top, "up"); File middle = createTestDir(up, "middle"); From 3b28836cda0444928fb043f143f0f16e928b79c5 Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Thu, 2 Jul 2015 17:56:32 +0300 Subject: [PATCH 094/137] IDEA-76396, IDEA-91965 precise mouse/trackpad scrolling in editor on Mac --- .../openapi/editor/impl/EditorImpl.java | 3 + .../impl/PreciseMouseWheelScroller.java | 164 ++++++++++++++++++ .../util/resources/misc/registry.properties | 3 + 3 files changed, 170 insertions(+) create mode 100644 platform/platform-impl/src/com/intellij/openapi/editor/impl/PreciseMouseWheelScroller.java diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index 32e2987f75cf..30fd0297d9d8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -516,6 +516,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi myEditorComponent = new EditorComponentImpl(this); myScrollPane = new MyScrollPane(); + if (SystemInfo.isMac && SystemInfo.isJavaVersionAtLeast("1.7") && Registry.is("editor.mac.smooth.scrolling")) { + PreciseMouseWheelScroller.install(myScrollPane); + } myVerticalScrollBar = (MyScrollBar)myScrollPane.getVerticalScrollBar(); myVerticalScrollBar.setOpaque(false); myPanel = new JPanel(); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/PreciseMouseWheelScroller.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/PreciseMouseWheelScroller.java new file mode 100644 index 000000000000..e3b446da58d8 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/PreciseMouseWheelScroller.java @@ -0,0 +1,164 @@ +/* + * 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.editor.impl; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.SystemInfo; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.lang.reflect.Method; + +/** + * Implements precise (with sub-line resolution) mouse wheel/trackpad scrolling in JScrollPane if platform supports it + * (currently known to work on Mac OS with Java 7 or later). + *

+ * Scroll pane's view is supposed to implement {@link Scrollable}. + * + * @see MouseWheelEvent#getPreciseWheelRotation() + */ +public class PreciseMouseWheelScroller implements MouseWheelListener { + private static final Logger LOGGER = Logger.getInstance(PreciseMouseWheelScroller.class); + private static Method PRECISE_WHEEL_ROTATION_GETTER; + + static { + if (SystemInfo.isJavaVersionAtLeast("1.7")) { + try { + Method method = MouseWheelEvent.class.getMethod("getPreciseWheelRotation"); + Class returnType = method.getReturnType(); + if (!returnType.equals(double.class)) throw new RuntimeException("Unexpected method return type: " + returnType); + PRECISE_WHEEL_ROTATION_GETTER = method; + } + catch (Exception e) { + LOGGER.warn("Couldn't access getPreciseWheelRotation method", e); + } + } + } + + public static void install(@NotNull JScrollPane scrollPane) { + scrollPane.setWheelScrollingEnabled(false); + scrollPane.addMouseWheelListener(new PreciseMouseWheelScroller(scrollPane)); + } + + private final JScrollPane myScrollPane; + + private PreciseMouseWheelScroller(JScrollPane scrollPane) { + myScrollPane = scrollPane; + } + + @Override + public void mouseWheelMoved(MouseWheelEvent e) { + e.consume(); + if (e.getWheelRotation() != 0) { + int orientation = SwingConstants.VERTICAL; + JScrollBar toScroll = myScrollPane.getVerticalScrollBar(); + if (toScroll == null || !toScroll.isVisible() || e.isShiftDown()) { + orientation = SwingConstants.HORIZONTAL; + toScroll = myScrollPane.getHorizontalScrollBar(); + if (toScroll == null || !toScroll.isVisible()) { + return; + } + } + + int direction = e.getWheelRotation() < 0 ? -1 : 1; + + if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) { + JViewport vp = myScrollPane.getViewport(); + Component comp = vp.getView(); + + boolean limitScroll = Math.abs(e.getWheelRotation()) == 1; + + Scrollable scrollComp = (Scrollable) comp; + Rectangle viewRect = vp.getViewRect(); + int scrollMin = toScroll.getMinimum(); + int scrollMax = toScroll.getMaximum() - toScroll.getModel().getExtent(); + + if (limitScroll) { + int blockIncr = scrollComp.getScrollableBlockIncrement(viewRect, orientation, direction); + if (direction < 0) { + scrollMin = Math.max(scrollMin, toScroll.getValue() - blockIncr); + } + else { + scrollMax = Math.min(scrollMax, toScroll.getValue() + blockIncr); + } + } + + double units = getUnitsToScroll(e); + double shift = scrollComp.getScrollableUnitIncrement(viewRect, orientation, direction) * units; + if (orientation == SwingConstants.VERTICAL) { + viewRect.y += shift; + if (direction < 0) { + if (viewRect.y <= scrollMin) { + viewRect.y = scrollMin; + } + } + else { + if (viewRect.y >= scrollMax) { + viewRect.y = scrollMax; + } + } + toScroll.setValue(viewRect.y); + } + else { + viewRect.x += shift; + if (direction < 0) { + if (viewRect.x < scrollMin) { + viewRect.x = scrollMin; + } + } + else { + if (viewRect.x > scrollMax) { + viewRect.x = scrollMax; + } + } + toScroll.setValue(viewRect.x); + } + } + else if (e.getScrollType() == MouseWheelEvent.WHEEL_BLOCK_SCROLL) { + int oldValue = toScroll.getValue(); + int blockIncrement = toScroll.getBlockIncrement(direction); + int delta = blockIncrement * direction; + int newValue = oldValue + delta; + + if (delta > 0 && newValue < oldValue) { + newValue = toScroll.getMaximum(); + } + else if (delta < 0 && newValue > oldValue) { + newValue = toScroll.getMinimum(); + } + + toScroll.setValue(newValue); + } + } + } + + // Required method was added in Java 7, but we need to compile for Java 6, so we resort to reflection + private static double getUnitsToScroll(MouseWheelEvent e) { + if (PRECISE_WHEEL_ROTATION_GETTER != null) { + try { + Double value = (Double)PRECISE_WHEEL_ROTATION_GETTER.invoke(e); + return e.getScrollAmount() * value; + } + catch (Exception ex) { + LOGGER.debug("Error calling getPreciseWheelRotation method", ex); + } + } + return e.getUnitsToScroll(); + } +} diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 12adcea3dfd7..90573c8892e3 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -144,6 +144,9 @@ editor.soft.wrap.force.limit.description=If document contains lines longer than editor.navigation.history.stack.size=25 editor.navigation.history.stack.size.description=Stack size limit for back/forward and last/next edit location navigation +editor.mac.smooth.scrolling=true +editor.mac.smooth.scrolling.description=Enable precise (with sub-line resolution) scrolling on Mac with mouse or trackpad + ide.showIndexRebuildMessage=false ide.tabbedPane.bufferedPaint=true From 80039be045f8141f9baba60bb82da4bae9ea3018 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Thu, 2 Jul 2015 16:44:55 +0200 Subject: [PATCH 095/137] IDEA-142127 When search preview is enabled, allow to change currently selected preview entry without leaving the search edit field --- .../com/intellij/find/impl/FindDialog.java | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java b/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java index ba00d930e2a3..1a997bd1d876 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java @@ -319,6 +319,67 @@ public class FindDialog extends DialogWrapper { } ); } + + if (!myModel.isReplaceState()) { + makeResultsPreviewActionOverride( + comboBox, + KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), + "choosePrevious", + new Runnable() { + @Override + public void run() { + int row = myResultsPreviewTable.getSelectedRow(); + if (row > 0) myResultsPreviewTable.setRowSelectionInterval(row - 1, row - 1); + } + } + ); + + makeResultsPreviewActionOverride( + comboBox, + KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), + "chooseNext", + new Runnable() { + @Override + public void run() { + int row = myResultsPreviewTable.getSelectedRow(); + if (row >= 0 && row + 1 < myResultsPreviewTable.getRowCount()) { + myResultsPreviewTable.setRowSelectionInterval(row + 1, row + 1); + } + } + } + ); + + new AnAction() { + @Override + public void actionPerformed(AnActionEvent e) { + if (myResultsPreviewTable != null && + myContent.getSelectedIndex() == RESULTS_PREVIEW_TAB_INDEX) { + navigateToSelectedUsage(myResultsPreviewTable); + } + } + }.registerCustomShortcutSet(CommonShortcuts.getEditSource(), comboBox); + } + } + + private void makeResultsPreviewActionOverride(final JComboBox component, KeyStroke keyStroke, String newActionKey, final Runnable newAction) { + InputMap inputMap = component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + Object action = inputMap.get(keyStroke); + inputMap.put(keyStroke, newActionKey); + final Action previousAction = action != null ? component.getActionMap().get(action) : null; + component.getActionMap().put(newActionKey, new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + if (previousAction != null && component.isPopupVisible()) { + previousAction.actionPerformed(e); + return; + } + + if(myResultsPreviewTable != null && + myContent.getSelectedIndex() == RESULTS_PREVIEW_TAB_INDEX) { + newAction.run(); + } + } + }); } private void handleComboBoxValueChanged(@NotNull ComboBox comboBox) { From 159af69b9a4e2c58390d9afc563a1f0b6213e4d9 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Thu, 2 Jul 2015 17:13:50 +0200 Subject: [PATCH 096/137] IDEA-142050 Search and Replace All not working --- .../com/intellij/find/FindManagerTest.java | 22 +++++++++++++++++++ .../src/com/intellij/find/FindUtil.java | 3 +-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java b/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java index 1fa5ce12b7f9..3f467403c061 100644 --- a/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java +++ b/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java @@ -461,6 +461,28 @@ public class FindManagerTest extends DaemonAnalyzerTestCase { assertEquals(text+"\n", getEditor().getDocument().getText()); } + public void testReplaceWithRegExp() { + FindModel findModel = new FindModel(); + findModel.setStringToFind("(? { diff --git a/platform/lang-impl/src/com/intellij/find/FindUtil.java b/platform/lang-impl/src/com/intellij/find/FindUtil.java index b60587f1d369..94ea1df621ca 100644 --- a/platform/lang-impl/src/com/intellij/find/FindUtil.java +++ b/platform/lang-impl/src/com/intellij/find/FindUtil.java @@ -531,8 +531,7 @@ public class FindUtil { String foundString = document.getCharsSequence().subSequence(startOffset, endOffset).toString(); String toReplace; try { - CharSequence textToMatch = document.getCharsSequence().subSequence(offset, document.getTextLength()); - toReplace = findManager.getStringToReplace(foundString, model, startOffset - offset, textToMatch); + toReplace = findManager.getStringToReplace(foundString, model, startOffset, document.getCharsSequence()); } catch (FindManager.MalformedReplacementStringException e) { if (!ApplicationManager.getApplication().isUnitTestMode()) { From 227f4e9dd1997af12520b4b47a8f0ba3ba0688ec Mon Sep 17 00:00:00 2001 From: Denis Fokin Date: Thu, 2 Jul 2015 18:35:42 +0300 Subject: [PATCH 097/137] IDEA-142216 Mac OS: "Platform and Plugins Updates" dialog (sheet message) doesn't have focus when shown from "Find Action" popup --- .../intellij/ide/util/gotoByName/ChooseByNameBase.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java index 8bea1a3318fe..40b722c51e6f 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java @@ -548,16 +548,13 @@ public abstract class ChooseByNameBase { EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue(); if (queue instanceof IdeEventQueue) { - if (!((IdeEventQueue)queue).wasRootRecentlyClicked(oppositeComponent)) { + if (((IdeEventQueue)queue).wasRootRecentlyClicked(oppositeComponent)) { Component root = SwingUtilities.getRoot(myTextField); - if (root != null && root.isShowing()) { - IdeFocusManager.getInstance(myProject).requestFocus(myTextField, true); - return; + if (root == null || root.isShowing()) { + hideHint(); } } } - - hideHint(); } } }, 5); From 4a3341e7bda0e648cedcaf4f967f65c54da42056 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Thu, 2 Jul 2015 18:44:56 +0300 Subject: [PATCH 098/137] PseudoFunctionStyleMethodInspection: detection of functional type improved --- .../PseudoLambdaReplaceTemplate.java | 65 +++++++------------ .../lambdaLibsStatic/simpleFind/test.java | 4 +- .../simpleFind/test_after.java | 2 +- 3 files changed, 28 insertions(+), 43 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/PseudoLambdaReplaceTemplate.java b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/PseudoLambdaReplaceTemplate.java index 92566655a237..53d1b2635b28 100644 --- a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/PseudoLambdaReplaceTemplate.java +++ b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/PseudoLambdaReplaceTemplate.java @@ -290,54 +290,39 @@ class PseudoLambdaReplaceTemplate { final @Nullable PsiSubstitutor methodSubstitutor, final PsiElement context) { if (type instanceof PsiClassType) { - PsiClass targetClass = ((PsiClassType)type).resolve(); - if (targetClass != null) { - //TODO fuu - if (targetClass instanceof PsiAnonymousClass) { - targetClass = ((PsiAnonymousClass)targetClass).getBaseClassType().resolve(); - if (targetClass == null) { + final PsiClass resolvedClass = ((PsiClassType)type).resolve(); + if (resolvedClass != null) { + if (resolvedClass instanceof PsiAnonymousClass) { + final PsiClass baseClass = ((PsiAnonymousClass)resolvedClass).getBaseClassType().resolve(); + if (baseClass == null) { return false; } + if (!LambdaUtil.isFunctionalClass(baseClass)) { + return false; + } + PsiMethod superMethod = LambdaUtil.getFunctionalInterfaceMethod(baseClass); + if (superMethod == null) { + return false; + } + final PsiMethod[] methods = resolvedClass.findMethodsByName(superMethod.getName(), false); + PsiMethod method = null; + for (PsiMethod m : methods) { + if (PsiSuperMethodUtil.isSuperMethod(m, superMethod)) { + method = m; + } + } + if (method == null) { + return false; + } + final PsiType psiType = methodSubstitutor == null ? method.getReturnType() : methodSubstitutor.substitute(method.getReturnType()); + return isSuitableLambdaRole(psiType, baseMethodReturnType, methodSubstitutor, context); } else { - if (!LambdaUtil.isFunctionalClass(targetClass)) { + if (!LambdaUtil.isFunctionalClass(resolvedClass)) { return false; } return isSuitableLambdaRole(LambdaUtil.getFunctionalInterfaceReturnType(type), baseMethodReturnType, methodSubstitutor, context); } - if (!LambdaUtil.isFunctionalClass(targetClass)) { - return false; } - //TODO fuu - PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(targetClass); - final PsiMethod[] methods = ((PsiClassType)type).resolve().findMethodsByName(method.getName(), false); - if (methods.length != 1) { - return false; - } - method = methods[0]; - if (method == null) { - return false; - } - final PsiType psiType; - if (type instanceof PsiClassReferenceType) { - final PsiJavaCodeReferenceElement reference = ((PsiClassReferenceType)type).getReference(); - final PsiClass resolvedClass = ((PsiClassReferenceType)type).resolve(); - final PsiTypeParameter[] typeParameters = resolvedClass.getTypeParameters(); - final PsiType[] substitutedTypeParameters = reference.getTypeParameters(); - - LOG.assertTrue(typeParameters.length == substitutedTypeParameters.length); - final Map substitutionMap = new HashMap(); - for (int i = 0; i < typeParameters.length; i++) { - PsiTypeParameter parameter = typeParameters[i]; - PsiType t = substitutedTypeParameters[i]; - substitutionMap.put(parameter, t); - } - PsiSubstitutor substitutor = PsiSubstitutorImpl.createSubstitutor(substitutionMap); - psiType = substitutor.substitute(method.getReturnType()); - } else { - psiType = method.getReturnType(); - } - return isSuitableLambdaRole(psiType, baseMethodReturnType, methodSubstitutor, context); - } return false; } else { return false; diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/simpleFind/test.java b/java/java-tests/testData/inspection/lambdaLibsStatic/simpleFind/test.java index 659f81f7be23..78cff8625ae8 100644 --- a/java/java-tests/testData/inspection/lambdaLibsStatic/simpleFind/test.java +++ b/java/java-tests/testData/inspection/lambdaLibsStatic/simpleFind/test.java @@ -6,9 +6,9 @@ import java.util.Collections; class c { void m() { - Iterables.find(Collections.emptyList(), new Predicate() { + Iterables.find(Collections.emptyList(), new Predicate() { @Override - public boolean apply(Object input) { + public boolean apply(String input) { return true; } }); diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/simpleFind/test_after.java b/java/java-tests/testData/inspection/lambdaLibsStatic/simpleFind/test_after.java index db6dff003227..9e43e688a5c6 100644 --- a/java/java-tests/testData/inspection/lambdaLibsStatic/simpleFind/test_after.java +++ b/java/java-tests/testData/inspection/lambdaLibsStatic/simpleFind/test_after.java @@ -6,6 +6,6 @@ import java.util.Collections; class c { void m() { - Collections.emptyList().stream().filter(input -> true).findFirst().get(); + Collections.emptyList().stream().filter(input -> true).findFirst().get(); } } \ No newline at end of file From ff134eb4fa055ce3b3c1ae4eaa122764c115e328 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Thu, 2 Jul 2015 18:33:58 +0300 Subject: [PATCH 099/137] Small optimization: do not retrieve JavaCodeStyleSettings on each JavaChildWrapArranger#arrange call --- .../intellij/psi/formatter/java/AbstractJavaBlock.java | 2 +- .../psi/formatter/java/wrap/JavaWrapManager.java | 9 +++++++-- .../formatter/java/wrap/impl/JavaChildWrapArranger.java | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java b/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java index fff50d1c41ab..6f74898b9a31 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java @@ -816,7 +816,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo @Nullable protected Wrap arrangeChildWrap(final ASTNode child, Wrap defaultWrap) { - return myWrapManager.arrangeChildWrap(child, myNode, getSettings(), defaultWrap, this); + return myWrapManager.arrangeChildWrap(child, myNode, mySettings, myJavaSettings, defaultWrap, this); } @NotNull diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/wrap/JavaWrapManager.java b/java/java-impl/src/com/intellij/psi/formatter/java/wrap/JavaWrapManager.java index 54965f9eca1e..c607722df701 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/wrap/JavaWrapManager.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/wrap/JavaWrapManager.java @@ -20,6 +20,7 @@ import com.intellij.formatting.Block; import com.intellij.formatting.Wrap; import com.intellij.lang.ASTNode; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import com.intellij.psi.codeStyle.JavaCodeStyleSettings; import com.intellij.psi.formatter.java.AbstractJavaBlock; import com.intellij.psi.formatter.java.wrap.impl.JavaChildBlockWrapFactory; import com.intellij.psi.formatter.java.wrap.impl.JavaChildWrapArranger; @@ -78,10 +79,14 @@ public class JavaWrapManager { * null otherwise */ @Nullable - public Wrap arrangeChildWrap(ASTNode child, ASTNode parent, CommonCodeStyleSettings settings, Wrap suggestedWrap, + public Wrap arrangeChildWrap(ASTNode child, + ASTNode parent, + CommonCodeStyleSettings settings, + JavaCodeStyleSettings javaSettings, + Wrap suggestedWrap, AbstractJavaBlock reservedWrapsProvider) { - return myChildArranger.arrange(child, parent, settings, suggestedWrap, reservedWrapsProvider); + return myChildArranger.arrange(child, parent, settings, javaSettings, suggestedWrap, reservedWrapsProvider); } /** diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildWrapArranger.java b/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildWrapArranger.java index 23914cb5fce8..8516ba04c067 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildWrapArranger.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildWrapArranger.java @@ -72,9 +72,9 @@ public class JavaChildWrapArranger { public Wrap arrange(ASTNode child, ASTNode parent, CommonCodeStyleSettings settings, + JavaCodeStyleSettings javaSettings, Wrap suggestedWrap, AbstractJavaBlock reservedWrapsProvider) { - final JavaCodeStyleSettings javaSettings = settings.getRootSettings().getCustomSettings(JavaCodeStyleSettings.class); ASTNode directParent = child.getTreeParent(); int role = ((CompositeElement)directParent).getChildRole(child); From 7bee09ce72aacfedcf4efc2132baa30ed11fa528 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Thu, 2 Jul 2015 17:38:00 +0200 Subject: [PATCH 100/137] write directly to zip out without intermediate files --- .../ide/plugins/PluginManagerCore.java | 11 ++- .../ide/actions/ExportSettingsAction.java | 83 ++++++++++++------- 2 files changed, 59 insertions(+), 35 deletions(-) diff --git a/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java b/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java index 41d7551234d9..73a0bbb993d8 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java +++ b/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java @@ -196,15 +196,18 @@ public class PluginManagerCore { if (!plugins.isFile()) { FileUtil.ensureCanCreateFile(plugins); } - PrintWriter printWriter = new PrintWriter(new BufferedWriter(new FileWriter(plugins, append))); + writePluginsList(ids, new BufferedWriter(new FileWriter(plugins, append))); + } + + public static void writePluginsList(@NotNull Collection ids, @NotNull Writer writer) throws IOException { try { for (String id : ids) { - printWriter.println(id); + writer.write(id); + writer.write(LineSeparator.getSystemLineSeparator().getSeparatorString()); } - printWriter.flush(); } finally { - printWriter.close(); + writer.close(); } } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/ExportSettingsAction.java b/platform/platform-impl/src/com/intellij/ide/actions/ExportSettingsAction.java index 6d753cf7bd01..b48f26647c1d 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/ExportSettingsAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/ExportSettingsAction.java @@ -40,7 +40,9 @@ import com.intellij.openapi.options.OptionsBundle; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.util.PairProcessor; import com.intellij.util.PlatformUtils; import com.intellij.util.ReflectionUtil; @@ -51,12 +53,10 @@ import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; +import java.io.*; import java.util.*; -import java.util.jar.JarOutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; public class ExportSettingsAction extends AnAction implements DumbAware { private static final Logger LOG = Logger.getInstance(ExportSettingsAction.class); @@ -85,34 +85,32 @@ public class ExportSettingsAction extends AnAction implements DumbAware { final File saveFile = dialog.getExportFile(); try { - if (saveFile.exists()) { - final int ret = Messages.showOkCancelDialog( - IdeBundle.message("prompt.overwrite.settings.file", FileUtil.toSystemDependentName(saveFile.getPath())), - IdeBundle.message("title.file.already.exists"), Messages.getWarningIcon()); - if (ret != Messages.OK) return; + if (saveFile.exists() && Messages.showOkCancelDialog( + IdeBundle.message("prompt.overwrite.settings.file", FileUtil.toSystemDependentName(saveFile.getPath())), + IdeBundle.message("title.file.already.exists"), Messages.getWarningIcon()) != Messages.OK) { + return; } - final JarOutputStream output = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(saveFile))); + + MyZipOutputStream zipOut = new MyZipOutputStream(new BufferedOutputStream(new FileOutputStream(saveFile))); try { - final File configPath = new File(PathManager.getConfigPath()); - final HashSet writtenItemRelativePaths = new HashSet(); + Set writtenItemRelativePaths = new THashSet(); + String configRoot = PathManager.getConfigPath(); for (File file : exportFiles) { - final String rPath = FileUtil.getRelativePath(configPath, file); - assert rPath != null; - final String relativePath = FileUtil.toSystemIndependentName(rPath); if (file.exists()) { - ZipUtil.addFileOrDirRecursively(output, saveFile, file, relativePath, null, writtenItemRelativePaths); + String rPath = FileUtilRt.getRelativePath(configRoot, file.getAbsolutePath(), File.separatorChar); + assert rPath != null; + ZipUtil.addFileOrDirRecursively(zipOut, null, file, FileUtilRt.toSystemIndependentName(rPath), null, writtenItemRelativePaths); } } - exportInstalledPlugins(saveFile, output, writtenItemRelativePaths); + exportInstalledPlugins(zipOut); - final File magicFile = new File(FileUtil.getTempDirectory(), ImportSettingsFilenameFilter.SETTINGS_JAR_MARKER); - FileUtil.createIfDoesntExist(magicFile); - magicFile.deleteOnExit(); - ZipUtil.addFileToZip(output, magicFile, ImportSettingsFilenameFilter.SETTINGS_JAR_MARKER, writtenItemRelativePaths, null); + ZipEntry zipEntry = new ZipEntry(ImportSettingsFilenameFilter.SETTINGS_JAR_MARKER); + zipOut.putNextEntry(zipEntry); + zipOut.closeEntry(); } finally { - output.close(); + zipOut.close(); } ShowFilePathAction.showDialog(getEventProject(e), IdeBundle.message("message.settings.exported.successfully"), IdeBundle.message("title.export.successful"), saveFile, null); @@ -122,18 +120,41 @@ public class ExportSettingsAction extends AnAction implements DumbAware { } } - private static void exportInstalledPlugins(File saveFile, JarOutputStream output, HashSet writtenItemRelativePaths) throws IOException { - final List oldPlugins = new ArrayList(); + private static void exportInstalledPlugins(@NotNull MyZipOutputStream zipOut) throws IOException { + List plugins = new ArrayList(); for (IdeaPluginDescriptor descriptor : PluginManagerCore.getPlugins()) { if (!descriptor.isBundled() && descriptor.isEnabled()) { - oldPlugins.add(descriptor.getPluginId().getIdString()); + plugins.add(descriptor.getPluginId().getIdString()); } } - if (!oldPlugins.isEmpty()) { - File tempFile = FileUtil.createTempFile("installed", "plugins"); - tempFile.deleteOnExit(); - PluginManagerCore.savePluginsList(oldPlugins, false, tempFile); - ZipUtil.addDirToZipRecursively(output, saveFile, tempFile, "/" + PluginManager.INSTALLED_TXT, null, writtenItemRelativePaths); + if (plugins.isEmpty()) { + return; + } + + ZipEntry e = new ZipEntry(PluginManager.INSTALLED_TXT); + zipOut.putNextEntry(e); + zipOut.ignoreClose = true; + try { + PluginManagerCore.writePluginsList(plugins, new OutputStreamWriter(zipOut, CharsetToolkit.UTF8_CHARSET)); + } + finally { + zipOut.ignoreClose = false; + zipOut.closeEntry(); + } + } + + private static class MyZipOutputStream extends ZipOutputStream { + private boolean ignoreClose = true; + + public MyZipOutputStream(@NotNull OutputStream out) { + super(out); + } + + @Override + public void close() throws IOException { + if (!ignoreClose) { + super.close(); + } } } From ec42ef32a20f3c893d0720220a1b43ede74dbd3c Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Thu, 2 Jul 2015 17:42:39 +0200 Subject: [PATCH 101/137] cleanup --- .../fileTemplates/impl/FileTemplateManagerImpl.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/FileTemplateManagerImpl.java b/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/FileTemplateManagerImpl.java index 43d75ffb9809..6b8a983f56eb 100644 --- a/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/FileTemplateManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/FileTemplateManagerImpl.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. @@ -47,16 +47,9 @@ import java.net.URL; import java.text.SimpleDateFormat; import java.util.*; -/** - * @author MYakovlev - * Date: Jul 24 - * @author 2002 - */ @State( name = "FileTemplateManagerImpl", - storages = { - @Storage(file = StoragePathMacros.WORKSPACE_FILE) - } + storages = @Storage(file = StoragePathMacros.WORKSPACE_FILE) ) public class FileTemplateManagerImpl extends FileTemplateManager implements PersistentStateComponent { private static final Logger LOG = Logger.getInstance("#com.intellij.ide.fileTemplates.impl.FileTemplateManagerImpl"); From ff4e0ec48de9716f10261f33dd3794fc89b20232 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Thu, 2 Jul 2015 17:43:42 +0200 Subject: [PATCH 102/137] cleanup --- .../intellij/ide/fileTemplates/impl/FTManager.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/FTManager.java b/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/FTManager.java index a4b9ed2d44aa..072ad6b61c58 100644 --- a/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/FTManager.java +++ b/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/FTManager.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. @@ -361,12 +361,9 @@ class FTManager { } public File getConfigRoot(boolean create) { - final File templatesPath = myTemplatesDir.isEmpty() ? new File(myScheme.getTemplatesDir()) : new File(myScheme.getTemplatesDir(), myTemplatesDir); - if (create) { - final boolean created = templatesPath.mkdirs(); - if (!created && !templatesPath.exists()) { - LOG.info("Cannot create directory: " + templatesPath.getAbsolutePath()); - } + File templatesPath = myTemplatesDir.isEmpty() ? new File(myScheme.getTemplatesDir()) : new File(myScheme.getTemplatesDir(), myTemplatesDir); + if (create && !templatesPath.mkdirs() && !templatesPath.exists()) { + LOG.info("Cannot create directory: " + templatesPath.getAbsolutePath()); } return templatesPath; } From 527708b0e7640a58e2ffbacbd1da7546be4498ef Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Thu, 2 Jul 2015 17:45:17 +0200 Subject: [PATCH 103/137] IDEA-138936 File templates are not imported via File/Import Settings --- .../impl/ExportableFileTemplateSettings.java | 29 ++++--------------- .../impl/FileTemplatesLoader.java | 4 +-- .../src/messages/OptionsBundle.properties | 3 +- .../src/META-INF/LangExtensions.xml | 4 +-- 4 files changed, 10 insertions(+), 30 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/ExportableFileTemplateSettings.java b/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/ExportableFileTemplateSettings.java index 55df13451829..db969cdf0762 100644 --- a/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/ExportableFileTemplateSettings.java +++ b/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/ExportableFileTemplateSettings.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 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. @@ -15,15 +15,12 @@ */ package com.intellij.ide.fileTemplates.impl; -import com.intellij.ide.IdeBundle; -import com.intellij.openapi.application.PathManager; import com.intellij.openapi.components.*; import com.intellij.openapi.fileTypes.ex.FileTypeManagerEx; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.File; import java.util.List; import java.util.Locale; @@ -33,13 +30,11 @@ import java.util.Locale; * @author Rustam Vishnyakov */ @State( - name="ExportableFileTemplateSettings", - storages= { - @Storage( - file = StoragePathMacros.APP_CONFIG + "/" + ExportableFileTemplateSettings.EXPORTABLE_SETTINGS_FILE - )} + name = "ExportableFileTemplateSettings", + storages = @Storage(file = StoragePathMacros.APP_CONFIG + "/" + ExportableFileTemplateSettings.EXPORTABLE_SETTINGS_FILE), + additionalExportFile = StoragePathMacros.ROOT_CONFIG + "/" + FileTemplatesLoader.TEMPLATES_DIR ) -public class ExportableFileTemplateSettings extends FileTemplatesLoader implements PersistentStateComponent, ExportableComponent { +public class ExportableFileTemplateSettings extends FileTemplatesLoader implements PersistentStateComponent { public final static String EXPORTABLE_SETTINGS_FILE = "file.template.settings.xml"; static final String ELEMENT_TEMPLATE = "template"; @@ -57,20 +52,6 @@ public class ExportableFileTemplateSettings extends FileTemplatesLoader implemen return ServiceManager.getService(ExportableFileTemplateSettings.class); } - @NotNull - @Override - public File[] getExportFiles() { - File exportableSettingsFile = - new File(PathManager.getOptionsPath() + File.separator + EXPORTABLE_SETTINGS_FILE); - return new File[] {getDefaultTemplatesManager().getConfigRoot(false), exportableSettingsFile}; - } - - @NotNull - @Override - public String getPresentableName() { - return IdeBundle.message("item.file.templates"); - } - @Nullable @Override public Element getState() { diff --git a/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/FileTemplatesLoader.java b/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/FileTemplatesLoader.java index a9423ad11f78..d9005b3e0093 100644 --- a/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/FileTemplatesLoader.java +++ b/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/FileTemplatesLoader.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. @@ -40,7 +40,7 @@ import java.util.*; public class FileTemplatesLoader { private static final Logger LOG = Logger.getInstance("#" + FileTemplatesLoader.class.getName()); - private static final String TEMPLATES_DIR = "fileTemplates"; + static final String TEMPLATES_DIR = "fileTemplates"; private static final String DEFAULT_TEMPLATES_ROOT = TEMPLATES_DIR; public static final String DESCRIPTION_FILE_EXTENSION = "html"; private static final String DESCRIPTION_EXTENSION_SUFFIX = "." + DESCRIPTION_FILE_EXTENSION; diff --git a/platform/platform-resources-en/src/messages/OptionsBundle.properties b/platform/platform-resources-en/src/messages/OptionsBundle.properties index a08997ceafa2..3a5edf93bb68 100644 --- a/platform/platform-resources-en/src/messages/OptionsBundle.properties +++ b/platform/platform-resources-en/src/messages/OptionsBundle.properties @@ -334,4 +334,5 @@ exportable.LafManager.presentable.name=Look and Feel exportable.EditorColorsManagerImpl.presentable.name=Editor Colors exportable.DiffManager.presentable.name=Diff exportable.DiffSettings.presentable.name=Diff -exportable.ExternalDiffSettings.presentable.name=External Diff \ No newline at end of file +exportable.ExternalDiffSettings.presentable.name=External Diff +exportable.ExportableFileTemplateSettings.presentable.name=File templates \ No newline at end of file diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index b77a4ea24061..3c32eea36435 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -27,9 +27,7 @@ - - + From b497d55356238f0595a7b775fa29aa9fca02e909 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Thu, 2 Jul 2015 11:59:14 +0200 Subject: [PATCH 104/137] IG: remove a little duplication --- ...inallyCanBeTryWithResourcesInspection.java | 51 +++++++------------ 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java index 2dd21f4b8ed7..6aa22a37e3ea 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java @@ -124,7 +124,7 @@ public class TryFinallyCanBeTryWithResourcesInspection extends BaseInspection { newTryStatementText.append(initializer.getText()); } else { - final int index = findInitialization(tryBlockChildren, variable, hasInitializer); + final int index = findInitialization(tryBlockChildren, variable, false); if (index < 0) { return; } @@ -253,26 +253,11 @@ public class TryFinallyCanBeTryWithResourcesInspection extends BaseInspection { if (rhs == null) { return false; } - final PsiElement variable; if (PsiType.NULL.equals(rhs.getType())) { - if (!(lhs instanceof PsiReferenceExpression)) { - return false; - } - final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)lhs; - variable = referenceExpression.resolve(); - if (!(variable instanceof PsiLocalVariable)) { - return false; - } + if (resolveLocalVariable(lhs) == null) return false; } else if (PsiType.NULL.equals(lhs.getType())) { - if (!(rhs instanceof PsiReferenceExpression)) { - return false; - } - final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)rhs; - variable = referenceExpression.resolve(); - if (!(variable instanceof PsiLocalVariable)) { - return false; - } + if (resolveLocalVariable(rhs) == null) return false; } else { return false; @@ -408,28 +393,17 @@ public class TryFinallyCanBeTryWithResourcesInspection extends BaseInspection { } final PsiElement variable; if (PsiType.NULL.equals(rhs.getType())) { - if (!(lhs instanceof PsiReferenceExpression)) { - return null; - } - final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)lhs; - variable = referenceExpression.resolve(); - if (!(variable instanceof PsiLocalVariable)) { - return null; - } + variable = resolveLocalVariable(lhs); } else if (PsiType.NULL.equals(lhs.getType())) { - if (!(rhs instanceof PsiReferenceExpression)) { - return null; - } - final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)rhs; - variable = referenceExpression.resolve(); - if (!(variable instanceof PsiLocalVariable)) { - return null; - } + variable = resolveLocalVariable(rhs); } else { return null; } + if (variable == null) { + return null; + } final PsiStatement thenBranch = ifStatement.getThenBranch(); final PsiLocalVariable resourceVariable; if (thenBranch instanceof PsiExpressionStatement) { @@ -577,4 +551,13 @@ public class TryFinallyCanBeTryWithResourcesInspection extends BaseInspection { return used; } } + + private static PsiLocalVariable resolveLocalVariable(PsiExpression expression) { + if (!(expression instanceof PsiReferenceExpression)) { + return null; + } + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)expression; + final PsiElement target = referenceExpression.resolve(); + return !(target instanceof PsiLocalVariable) ? null : (PsiLocalVariable)target; + } } From 9b9480feea93507cc2ed0afd78e831057203304c Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Thu, 2 Jul 2015 12:10:42 +0200 Subject: [PATCH 105/137] IG: check for reassignment instead of if variable can be final --- ...inallyCanBeTryWithResourcesInspection.java | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java index 6aa22a37e3ea..8edfb909fd47 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java @@ -27,8 +27,8 @@ import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; -import com.siyeh.ig.psiutils.FinalUtils; import com.siyeh.ig.psiutils.PsiElementOrderComparator; +import com.siyeh.ig.psiutils.VariableAccessUtils; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -338,9 +338,6 @@ public class TryFinallyCanBeTryWithResourcesInspection extends BaseInspection { } static boolean isVariableUsedOutsideContext(PsiVariable variable, PsiElement context) { - if (!FinalUtils.canBeFinal(variable)) { - return true; - } final VariableUsedOutsideContextVisitor visitor = new VariableUsedOutsideContextVisitor(variable, context); final PsiElement declarationScope = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class); if (declarationScope == null) { @@ -465,37 +462,46 @@ public class TryFinallyCanBeTryWithResourcesInspection extends BaseInspection { return aClass != null && InheritanceUtil.isInheritor(aClass, CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE); } - static int findInitialization(PsiElement[] elements, PsiVariable variable, - boolean hasInitializer) { + static int findInitialization(PsiElement[] elements, PsiVariable variable, boolean hasInitializer) { int result = -1; final int statementsLength = elements.length; for (int i = 0; i < statementsLength; i++) { final PsiElement element = elements[i]; - if (!(element instanceof PsiExpressionStatement)) { - continue; - } - final PsiExpressionStatement expressionStatement = (PsiExpressionStatement)element; - final PsiExpression expression = expressionStatement.getExpression(); - if (!(expression instanceof PsiAssignmentExpression)) { - continue; - } - final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expression; - final PsiExpression lhs = assignmentExpression.getLExpression(); - if (!(lhs instanceof PsiReferenceExpression)) { - continue; - } - final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)lhs; - final PsiElement target = referenceExpression.resolve(); - if (variable.equals(target)) { + if (isNormalAssignment(element, variable)) { if (result >= 0 && !hasInitializer) { return -1; } + else if (hasInitializer) { + return i; + } result = i; } + else if (VariableAccessUtils.variableIsAssigned(variable, element)) { + return hasInitializer ? i : -1; + } } return result; } + private static boolean isNormalAssignment(PsiElement element, PsiVariable variable) { + if (!(element instanceof PsiExpressionStatement)) { + return false; + } + final PsiExpressionStatement expressionStatement = (PsiExpressionStatement)element; + final PsiExpression expression = expressionStatement.getExpression(); + if (!(expression instanceof PsiAssignmentExpression)) { + return false; + } + final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expression; + final PsiExpression lhs = assignmentExpression.getLExpression(); + if (!(lhs instanceof PsiReferenceExpression)) { + return false; + } + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)lhs; + final PsiElement target = referenceExpression.resolve(); + return variable.equals(target); + } + static class VariableUsedOutsideContextVisitor extends JavaRecursiveElementVisitor { private boolean used = false; From 67520a55f5dccfe6283eb364c82256051aae143a Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Thu, 2 Jul 2015 12:34:19 +0200 Subject: [PATCH 106/137] IG: fix "'try finally' replaceable with 'try' with resources" inspection problem --- .../TryFinallyCanBeTryWithResourcesInspection.java | 12 ++---------- .../TryFinallyCanBeTryWithResources.java | 13 +++++++++++++ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java index 8edfb909fd47..2f4238879681 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java @@ -310,7 +310,6 @@ public class TryFinallyCanBeTryWithResourcesInspection extends BaseInspection { return; } final PsiStatement[] tryBlockStatements = tryBlock.getStatements(); - boolean found = false; for (PsiVariable variable : variables) { final boolean hasInitializer; final PsiExpression initializer = variable.getInitializer(); @@ -322,17 +321,10 @@ public class TryFinallyCanBeTryWithResourcesInspection extends BaseInspection { hasInitializer = !PsiType.NULL.equals(type); } final int index = findInitialization(tryBlockStatements, variable, hasInitializer); - if (index >= 0 ^ hasInitializer) { - if (isVariableUsedOutsideContext(variable, tryBlock)) { - continue; - } - found = true; - break; + if (!(index >= 0 ^ hasInitializer) || isVariableUsedOutsideContext(variable, tryBlock)) { + return; } } - if (!found) { - return; - } registerStatementError(tryStatement); } } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/migration/try_finally_can_be_try_with_resources/TryFinallyCanBeTryWithResources.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/migration/try_finally_can_be_try_with_resources/TryFinallyCanBeTryWithResources.java index 83cd11796df7..63b913633e2d 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/migration/try_finally_can_be_try_with_resources/TryFinallyCanBeTryWithResources.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/migration/try_finally_can_be_try_with_resources/TryFinallyCanBeTryWithResources.java @@ -49,4 +49,17 @@ class TryFinallyCanBeTryWithResources { in.close(); } } + + public void read4() throws IOException { + FileInputStream fileInputStream = null; + FileInputStream bufferedInputStream = null; + try { + fileInputStream = new FileInputStream("s"); + bufferedInputStream = null; // don't report, one of the vars is reassigned + bufferedInputStream = new FileInputStream("fileInputStream"); + } finally { + bufferedInputStream.close(); + fileInputStream.close(); + } + } } \ No newline at end of file From 3e428407ca8fe644be0a99802e7f2df6ef3416b3 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Thu, 2 Jul 2015 14:56:30 +0200 Subject: [PATCH 107/137] IG: fix "'try finally' replaceable with 'try' with resources" inspection quickfix problem --- .../TryFinallyCanBeTryWithResourcesInspection.java | 6 ++++++ .../Dependents.after.java | 9 +++++++++ .../Dependents.java | 13 +++++++++++++ .../TryFinallyCanBeTryWithResourcesFixTest.java | 1 + 4 files changed, 29 insertions(+) create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/migration/try_finally_can_be_try_with_resources/Dependents.after.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/migration/try_finally_can_be_try_with_resources/Dependents.java diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java index 2f4238879681..69607a9150d7 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java @@ -142,6 +142,12 @@ public class TryFinallyCanBeTryWithResourcesInspection extends BaseInspection { } separator = true; } + int j = 1; + while (!unwantedChildren.contains(Integer.valueOf(j)) && j < tryBlockChildren.length - 1) { + tryStatement.getParent().addBefore(tryBlockChildren[j], tryStatement); + unwantedChildren.add(Integer.valueOf(j)); + j++; + } newTryStatementText.append(") {"); final int tryBlockStatementsLength = tryBlockChildren.length - 1; for (int i = 1; i < tryBlockStatementsLength; i++) { diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/migration/try_finally_can_be_try_with_resources/Dependents.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/migration/try_finally_can_be_try_with_resources/Dependents.after.java new file mode 100644 index 000000000000..9b6dcdf2ed0f --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/migration/try_finally_can_be_try_with_resources/Dependents.after.java @@ -0,0 +1,9 @@ +import java.io.*; + +class Dependents { + void m() throws IOException { + String filename = "filename"; + try (InputStream in = new FileInputStream(filename)) { + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/migration/try_finally_can_be_try_with_resources/Dependents.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/migration/try_finally_can_be_try_with_resources/Dependents.java new file mode 100644 index 000000000000..bacd212ac072 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/migration/try_finally_can_be_try_with_resources/Dependents.java @@ -0,0 +1,13 @@ +import java.io.*; + +class Dependents { + void m() throws IOException { + InputStream in = null; + try { + String filename = "filename"; + in = new FileInputStream(filename); + }finally{ + in.close(); + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/migration/TryFinallyCanBeTryWithResourcesFixTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/migration/TryFinallyCanBeTryWithResourcesFixTest.java index fcfcbdad0761..766077afdebd 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/migration/TryFinallyCanBeTryWithResourcesFixTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/migration/TryFinallyCanBeTryWithResourcesFixTest.java @@ -23,6 +23,7 @@ public class TryFinallyCanBeTryWithResourcesFixTest extends IGQuickFixesTestCase public void testComments() { doTest(); } public void testMultiple() { doTest(); } + public void testDependents() { doTest(); } @Override public void setUp() throws Exception { From 85b59f6b7077bdbdc11cc4a55857278d8399777c Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Thu, 2 Jul 2015 15:00:04 +0200 Subject: [PATCH 108/137] IG: remove a little duplication --- .../TryWithIdenticalCatchesInspection.java | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryWithIdenticalCatchesInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryWithIdenticalCatchesInspection.java index 8f5242aa27cf..735bf854ee58 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryWithIdenticalCatchesInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryWithIdenticalCatchesInspection.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. @@ -117,7 +117,7 @@ public class TryWithIdenticalCatchesInspection extends BaseInspection { if (parameterValues != null && (parameterValues.size() != 1 || !(parameterValues.get(0) instanceof PsiReferenceExpression))) { continue; } - if (!canCollapse(parameters, i, j)) { + if (j > i ? !canCollapse(parameters, i, j) : !canCollapse(parameters, j, i)) { continue; } final PsiJavaToken rParenth = otherSection.getRParenth(); @@ -132,26 +132,15 @@ public class TryWithIdenticalCatchesInspection extends BaseInspection { } private static boolean canCollapse(PsiParameter[] parameters, int index1, int index2) { - if (index2 > index1) { - final PsiType type = parameters[index2].getType(); - for (int i = index1 + 1; i < index2; i++) { - final PsiType otherType = parameters[i].getType(); - if (TypeConversionUtil.isAssignable(type, otherType)) { - return false; - } + if (index2 <= index1) throw new IllegalArgumentException(); + final PsiType type = parameters[index2].getType(); + for (int i = index1 + 1; i < index2; i++) { + final PsiType otherType = parameters[i].getType(); + if (TypeConversionUtil.isAssignable(type, otherType)) { + return false; } - return true; - } - else { - final PsiType type = parameters[index1].getType(); - for (int i = index2 + 1; i < index1; i++) { - final PsiType otherType = parameters[i].getType(); - if (TypeConversionUtil.isAssignable(otherType, type)) { - return false; - } - } - return true; } + return true; } } From 899954178f29cbbb60cafaaa4181a9c12551fc11 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Thu, 2 Jul 2015 15:17:43 +0200 Subject: [PATCH 109/137] IG: remove some more duplication --- .../siyeh/ig/psiutils/InstanceOfUtils.java | 31 +++---------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/InstanceOfUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/InstanceOfUtils.java index e9d415473361..614c39494ea1 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/InstanceOfUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/InstanceOfUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2007-2011 Bas Leijdekkers + * Copyright 2007-2015 Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -156,35 +156,14 @@ public class InstanceOfUtils { } } } - PsiExpression condition = ifStatement.getCondition(); - condition = PsiUtil.deparenthesizeExpression(condition); - if (condition instanceof PsiPolyadicExpression) { - final PsiPolyadicExpression binaryExpression = - (PsiPolyadicExpression)condition; - visitPolyadicExpression(binaryExpression); - } - else { - checkExpression(condition); - } + checkExpression(ifStatement.getCondition()); } @Override public void visitConditionalExpression(PsiConditionalExpression expression) { - final PsiExpression elseExpression = - expression.getElseExpression(); - inElse = elseExpression != null && - PsiTreeUtil.isAncestor(elseExpression, - referenceExpression, true); - PsiExpression condition = expression.getCondition(); - condition = PsiUtil.deparenthesizeExpression(condition); - if (condition instanceof PsiPolyadicExpression) { - final PsiPolyadicExpression binaryExpression = - (PsiPolyadicExpression)condition; - visitPolyadicExpression(binaryExpression); - } - else { - checkExpression(condition); - } + final PsiExpression elseExpression = expression.getElseExpression(); + inElse = elseExpression != null && PsiTreeUtil.isAncestor(elseExpression, referenceExpression, true); + checkExpression(expression.getCondition()); } private void checkExpression(PsiExpression expression) { From ffded1998d8bdc140bcea56b7ee581a38fee40f3 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Thu, 2 Jul 2015 17:22:13 +0200 Subject: [PATCH 110/137] make IG test light --- .../andAnd/expected.xml | 4 --- .../elseElse/expected.xml | 3 -- .../elseElseOrOr/expected.xml | 3 -- .../notOr/expected.xml | 3 -- .../orInstanceofOrInstanceof/expected.xml | 4 --- .../polyadic/expected.xml | 3 -- .../simple/expected.xml | 8 ------ .../AndAnd.java | 0 .../ElseElse.java | 2 +- .../ElseElseOrOr.java} | 2 +- .../NotOr.java | 0 .../OrInstanceofOrInstanceof.java | 0 .../Polyadic.java} | 4 +-- .../Simple.java} | 4 +-- ...ConflictsWithInstanceofInspectionTest.java | 28 +++++++++++-------- 15 files changed, 22 insertions(+), 46 deletions(-) delete mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/andAnd/expected.xml delete mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/elseElse/expected.xml delete mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/elseElseOrOr/expected.xml delete mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/notOr/expected.xml delete mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/orInstanceofOrInstanceof/expected.xml delete mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/polyadic/expected.xml delete mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/simple/expected.xml rename plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/{castConflictingInstanceof/andAnd => cast_conflicts_with_instanceof}/AndAnd.java (100%) rename plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/{castConflictingInstanceof/elseElse => cast_conflicts_with_instanceof}/ElseElse.java (97%) rename plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/{castConflictingInstanceof/elseElseOrOr/ElseElse.java => cast_conflicts_with_instanceof/ElseElseOrOr.java} (97%) rename plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/{castConflictingInstanceof/notOr => cast_conflicts_with_instanceof}/NotOr.java (100%) rename plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/{castConflictingInstanceof/orInstanceofOrInstanceof => cast_conflicts_with_instanceof}/OrInstanceofOrInstanceof.java (100%) rename plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/{castConflictingInstanceof/polyadic/ElseElse.java => cast_conflicts_with_instanceof/Polyadic.java} (74%) rename plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/{castConflictingInstanceof/simple/ElseElse.java => cast_conflicts_with_instanceof/Simple.java} (86%) diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/andAnd/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/andAnd/expected.xml deleted file mode 100644 index e8f00e82629f..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/andAnd/expected.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/elseElse/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/elseElse/expected.xml deleted file mode 100644 index 580a889dbfd6..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/elseElse/expected.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/elseElseOrOr/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/elseElseOrOr/expected.xml deleted file mode 100644 index 580a889dbfd6..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/elseElseOrOr/expected.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/notOr/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/notOr/expected.xml deleted file mode 100644 index 580a889dbfd6..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/notOr/expected.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/orInstanceofOrInstanceof/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/orInstanceofOrInstanceof/expected.xml deleted file mode 100644 index e8f00e82629f..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/orInstanceofOrInstanceof/expected.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/polyadic/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/polyadic/expected.xml deleted file mode 100644 index 580a889dbfd6..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/polyadic/expected.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/simple/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/simple/expected.xml deleted file mode 100644 index 296b4dd3fe90..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/simple/expected.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - ElseElse.java - 26 - Cast <code>(B)o</code> conflicts with surrounding 'instanceof' check #loc - - diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/andAnd/AndAnd.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/AndAnd.java similarity index 100% rename from plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/andAnd/AndAnd.java rename to plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/AndAnd.java diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/elseElse/ElseElse.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/ElseElse.java similarity index 97% rename from plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/elseElse/ElseElse.java rename to plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/ElseElse.java index fc68cfe0d3aa..02fa4cc29feb 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/elseElse/ElseElse.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/ElseElse.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -public interface I {} +interface I {} class A implements I {} class B implements I {} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/elseElseOrOr/ElseElse.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/ElseElseOrOr.java similarity index 97% rename from plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/elseElseOrOr/ElseElse.java rename to plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/ElseElseOrOr.java index 657c588cbbfa..a65557269f2f 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/elseElseOrOr/ElseElse.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/ElseElseOrOr.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -public interface I {} +interface I {} class A implements I {} class B implements I {} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/notOr/NotOr.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/NotOr.java similarity index 100% rename from plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/notOr/NotOr.java rename to plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/NotOr.java diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/orInstanceofOrInstanceof/OrInstanceofOrInstanceof.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/OrInstanceofOrInstanceof.java similarity index 100% rename from plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/orInstanceofOrInstanceof/OrInstanceofOrInstanceof.java rename to plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/OrInstanceofOrInstanceof.java diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/polyadic/ElseElse.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/Polyadic.java similarity index 74% rename from plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/polyadic/ElseElse.java rename to plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/Polyadic.java index ae955eb8b94c..582ddfff3b75 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/polyadic/ElseElse.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/Polyadic.java @@ -1,4 +1,4 @@ -public interface I {} +interface I {} class A implements I {} class B implements I {} @@ -6,7 +6,7 @@ class U { void foo(Object o) { boolean b = false; if (o instanceof I) { - b = o instanceof A && bar((A)a) && !bazz((A)a) + b = o instanceof A && bar((A)o) && !bazz((A)o); } } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/simple/ElseElse.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/Simple.java similarity index 86% rename from plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/simple/ElseElse.java rename to plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/Simple.java index 9c10e992a4ef..0334745f49a7 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/castConflictingInstanceof/simple/ElseElse.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/Simple.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -public interface I {} +interface I {} class A implements I {} class B implements I {} @@ -23,7 +23,7 @@ class U { } else { if (o instanceof A) { - B bb = (B)o; + B bb = (B)o; } else { } } diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/CastConflictsWithInstanceofInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/CastConflictsWithInstanceofInspectionTest.java index 079bda6ac35f..369e2f48e1e9 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/CastConflictsWithInstanceofInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/CastConflictsWithInstanceofInspectionTest.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. @@ -15,39 +15,43 @@ */ package com.siyeh.ig.bugs; -import com.siyeh.ig.IGInspectionTestCase; +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.Nullable; -public class CastConflictsWithInstanceofInspectionTest extends IGInspectionTestCase { +public class CastConflictsWithInstanceofInspectionTest extends LightInspectionTestCase { - public void testElseElse() throws Exception { + public void testElseElse() { doTest(); } - public void testSimple() throws Exception { + public void testSimple() { doTest(); } - public void testElseElseOrOr() throws Exception { + public void testElseElseOrOr() { doTest(); } - public void testAndAnd() throws Exception { + public void testAndAnd() { doTest(); } - public void testPolyadic() throws Exception { + public void testPolyadic() { doTest(); } - public void testNotOr() throws Exception { + public void testNotOr() { doTest(); } - public void testOrInstanceofOrInstanceof() throws Exception { + public void testOrInstanceofOrInstanceof() { doTest(); } - private void doTest() throws Exception { - doTest("com/siyeh/igtest/bugs/castConflictingInstanceof/" + getTestName(true), new CastConflictsWithInstanceofInspection()); + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + return new CastConflictsWithInstanceofInspection(); } } From f25a9a7ebe5dd5928629f3f628e778e56e46e94c Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Thu, 2 Jul 2015 17:43:11 +0200 Subject: [PATCH 111/137] IDEA-142002 (Invalid CastConflictsWithInstanceof warning) --- .../siyeh/ig/psiutils/InstanceOfUtils.java | 53 +++++++++++++++---- .../replaceInstanceofInFront.after.java | 8 +++ .../replaceInstanceofInFront.java | 8 +++ .../AssertCheckBefore.java | 10 ++++ .../IfCheckBefore.java | 12 +++++ .../IfElseCheckBefore.java | 15 ++++++ ...ConflictsWithInstanceofInspectionTest.java | 12 +++++ .../CastConflictsWithInstanceofFixesTest.java | 4 ++ 8 files changed, 113 insertions(+), 9 deletions(-) create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/bugs/castConflicts/replaceInstanceofInFront.after.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/bugs/castConflicts/replaceInstanceofInFront.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/AssertCheckBefore.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/IfCheckBefore.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/IfElseCheckBefore.java diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/InstanceOfUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/InstanceOfUtils.java index 614c39494ea1..21aa528fadf3 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/InstanceOfUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/InstanceOfUtils.java @@ -35,6 +35,41 @@ public class InstanceOfUtils { } final PsiClassType rawType = classType.rawType(); final InstanceofChecker checker = new InstanceofChecker(operand, rawType, false); + PsiStatement sibling = PsiTreeUtil.getParentOfType(context, PsiStatement.class); + sibling = PsiTreeUtil.getPrevSiblingOfType(sibling, PsiStatement.class); + while (sibling != null) { + if (sibling instanceof PsiIfStatement) { + final PsiIfStatement ifStatement = (PsiIfStatement)sibling; + final PsiExpression condition = ifStatement.getCondition(); + if (condition != null) { + if (!ControlFlowUtils.statementMayCompleteNormally(ifStatement.getThenBranch())) { + checker.negate = true; + checker.checkExpression(condition); + if (checker.hasAgreeingInstanceof()) { + return null; + } + } + else if (!ControlFlowUtils.statementMayCompleteNormally(ifStatement.getElseBranch())) { + checker.negate = false; + checker.checkExpression(condition); + if (checker.hasAgreeingInstanceof()) { + return null; + } + } + } + } + else if (sibling instanceof PsiAssertStatement) { + final PsiAssertStatement assertStatement = (PsiAssertStatement)sibling; + final PsiExpression condition = assertStatement.getAssertCondition(); + checker.negate = false; + checker.checkExpression(condition); + if (checker.hasAgreeingInstanceof()) { + return null; + } + } + sibling = PsiTreeUtil.getPrevSiblingOfType(sibling, PsiStatement.class); + } + checker.negate = false; PsiElement parent = PsiTreeUtil.getParentOfType(context, PsiIfStatement.class, PsiConditionalExpression.class, PsiPolyadicExpression.class); while (parent != null) { @@ -82,7 +117,7 @@ public class InstanceOfUtils { private final PsiReferenceExpression referenceExpression; private final PsiType castType; private final boolean strict; - private boolean inElse = false; + private boolean negate = false; private PsiInstanceOfExpression conflictingInstanceof = null; private boolean agreeingInstanceof = false; @@ -110,21 +145,21 @@ public class InstanceOfUtils { return; } } - if (!inElse && conflictingInstanceof != null) { + if (!negate && conflictingInstanceof != null) { agreeingInstanceof = false; } } else if (tokenType == JavaTokenType.OROR) { for (PsiExpression operand : expression.getOperands()) { if (operand instanceof PsiPrefixExpression && ((PsiPrefixExpression)operand).getOperationTokenType() == JavaTokenType.EXCL) { - inElse = true; + negate = true; } checkExpression(operand); if (agreeingInstanceof) { return; } } - if (inElse && conflictingInstanceof != null) { + if (negate && conflictingInstanceof != null) { agreeingInstanceof = false; } } @@ -133,9 +168,9 @@ public class InstanceOfUtils { @Override public void visitIfStatement(PsiIfStatement ifStatement) { final PsiStatement branch = ifStatement.getElseBranch(); - inElse = branch != null && + negate = branch != null && PsiTreeUtil.isAncestor(branch, referenceExpression, true); - if (inElse) { + if (negate) { if (branch instanceof PsiBlockStatement) { final PsiBlockStatement blockStatement = (PsiBlockStatement)branch; @@ -162,13 +197,13 @@ public class InstanceOfUtils { @Override public void visitConditionalExpression(PsiConditionalExpression expression) { final PsiExpression elseExpression = expression.getElseExpression(); - inElse = elseExpression != null && PsiTreeUtil.isAncestor(elseExpression, referenceExpression, true); + negate = elseExpression != null && PsiTreeUtil.isAncestor(elseExpression, referenceExpression, true); checkExpression(expression.getCondition()); } private void checkExpression(PsiExpression expression) { expression = PsiUtil.deparenthesizeExpression(expression); - if (inElse) { + if (negate) { if (expression instanceof PsiPrefixExpression) { final PsiPrefixExpression prefixExpression = (PsiPrefixExpression)expression; @@ -202,7 +237,7 @@ public class InstanceOfUtils { agreeingInstanceof = true; conflictingInstanceof = null; } - else if (isConflicting(instanceOfExpression)) { + else if (isConflicting(instanceOfExpression) && conflictingInstanceof == null) { conflictingInstanceof = instanceOfExpression; } } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/bugs/castConflicts/replaceInstanceofInFront.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/bugs/castConflicts/replaceInstanceofInFront.after.java new file mode 100644 index 000000000000..493e7b34b5e0 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/bugs/castConflicts/replaceInstanceofInFront.after.java @@ -0,0 +1,8 @@ +public class Test { + void foo(Object o) { + if (o instanceof Number) { + assert o instanceof Integer; + Integer i = (Integer)o; + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/bugs/castConflicts/replaceInstanceofInFront.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/bugs/castConflicts/replaceInstanceofInFront.java new file mode 100644 index 000000000000..581b710f39ae --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/bugs/castConflicts/replaceInstanceofInFront.java @@ -0,0 +1,8 @@ +public class Test { + void foo(Object o) { + if (o instanceof Number) { + assert o instanceof String; + Integer i = (Integer)o; + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/AssertCheckBefore.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/AssertCheckBefore.java new file mode 100644 index 000000000000..566ae6107a93 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/AssertCheckBefore.java @@ -0,0 +1,10 @@ +class AssertCheckBefore { + void m(Object child, Object parent) { + if (parent instanceof Number) { + if (child instanceof String) { + assert parent instanceof Integer; + Integer attribute = (Integer) parent; + } + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/IfCheckBefore.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/IfCheckBefore.java new file mode 100644 index 000000000000..5dc197d90fbe --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/IfCheckBefore.java @@ -0,0 +1,12 @@ +class IfCheckBefore { + void m(Object child, Object parent) { + if (parent instanceof Number) { + if (child instanceof String) { + if (!(parent instanceof Integer)) { + return; + } + Integer attribute = (Integer) parent; + } + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/IfElseCheckBefore.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/IfElseCheckBefore.java new file mode 100644 index 000000000000..7303650d78d9 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/cast_conflicts_with_instanceof/IfElseCheckBefore.java @@ -0,0 +1,15 @@ +class IfCheckBefore { + void m(Object child, Object parent) { + if (parent instanceof Number) { + if (child instanceof String) { + if ((parent instanceof Integer)) { + System.out.println(parent); + } + else { + return; + } + Integer attribute = (Integer) parent; + } + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/CastConflictsWithInstanceofInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/CastConflictsWithInstanceofInspectionTest.java index 369e2f48e1e9..2876ea89c956 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/CastConflictsWithInstanceofInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/CastConflictsWithInstanceofInspectionTest.java @@ -49,6 +49,18 @@ public class CastConflictsWithInstanceofInspectionTest extends LightInspectionTe doTest(); } + public void testIfCheckBefore() { + doTest(); + } + + public void testIfElseCheckBefore() { + doTest(); + } + + public void testAssertCheckBefore() { + doTest(); + } + @Nullable @Override protected InspectionProfileEntry getInspection() { diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/bugs/CastConflictsWithInstanceofFixesTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/bugs/CastConflictsWithInstanceofFixesTest.java index d97b55a40036..26cee5c63a7e 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/bugs/CastConflictsWithInstanceofFixesTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/bugs/CastConflictsWithInstanceofFixesTest.java @@ -49,6 +49,10 @@ public class CastConflictsWithInstanceofFixesTest extends IGQuickFixesTestCase { assertQuickfixNotAvailable("Replace 'E' with 'String' in cast"); } + public void testReplaceInstanceofInFront() { + doTest("replaceInstanceofInFront", "Replace 'String' with 'Integer' in instanceof"); + } + @Override protected String getRelativePath() { return "bugs/castConflicts"; From d4620677e5ed1bf7f59c54706d09d5f458b0d6a8 Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 2 Jul 2015 16:03:08 +0200 Subject: [PATCH 112/137] IDEA-141973 Java else if: missing select word step --- .../wordSelection/IfStatementSelectioner.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/editorActions/wordSelection/IfStatementSelectioner.java b/java/java-impl/src/com/intellij/codeInsight/editorActions/wordSelection/IfStatementSelectioner.java index 2acc887e1678..89cb30a2a305 100644 --- a/java/java-impl/src/com/intellij/codeInsight/editorActions/wordSelection/IfStatementSelectioner.java +++ b/java/java-impl/src/com/intellij/codeInsight/editorActions/wordSelection/IfStatementSelectioner.java @@ -40,6 +40,14 @@ public class IfStatementSelectioner extends BasicSelectioner { final PsiKeyword elseKeyword = statement.getElseElement(); if (elseKeyword != null) { + final PsiStatement then = statement.getThenBranch(); + if (then != null) { + final TextRange thenRange = new TextRange(statement.getTextRange().getStartOffset(), then.getTextRange().getEndOffset()); + if (thenRange.contains(cursorOffset)) { + result.addAll(expandToWholeLine(editorText, thenRange, false)); + } + } + result.addAll(expandToWholeLine(editorText, new TextRange(elseKeyword.getTextRange().getStartOffset(), statement.getTextRange().getEndOffset()), @@ -50,10 +58,13 @@ public class IfStatementSelectioner extends BasicSelectioner { PsiIfStatement elseIf = (PsiIfStatement)branch; final PsiKeyword element = elseIf.getElseElement(); if (element != null) { - result.addAll(expandToWholeLine(editorText, - new TextRange(elseKeyword.getTextRange().getStartOffset(), - elseIf.getThenBranch().getTextRange().getEndOffset()), - false)); + final PsiStatement elseThen = elseIf.getThenBranch(); + if (elseThen != null) { + result.addAll(expandToWholeLine(editorText, + new TextRange(elseKeyword.getTextRange().getStartOffset(), + elseThen.getTextRange().getEndOffset()), + false)); + } } } } From f56d41321192c71379217cb48848e577249e0e5c Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 2 Jul 2015 16:51:55 +0200 Subject: [PATCH 113/137] extract LiveTemplateTree class --- .../template/impl/LiveTemplateTree.java | 64 +++++++++++++++++++ .../template/impl/TemplateListPanel.java | 49 +++----------- 2 files changed, 74 insertions(+), 39 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/codeInsight/template/impl/LiveTemplateTree.java diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/LiveTemplateTree.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/LiveTemplateTree.java new file mode 100644 index 000000000000..150b87bc6056 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/LiveTemplateTree.java @@ -0,0 +1,64 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.impl; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.CheckboxTree; +import com.intellij.ui.CheckedTreeNode; +import com.intellij.ui.TreeSpeedSearch; +import com.intellij.util.containers.Convertor; + +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreePath; + +/** + * @author peter + */ +class LiveTemplateTree extends CheckboxTree { + LiveTemplateTree(final CheckboxTreeCellRenderer renderer, final CheckedTreeNode root) { + super(renderer, root); + } + + @Override + protected void onNodeStateChanged(final CheckedTreeNode node) { + Object obj = node.getUserObject(); + if (obj instanceof TemplateImpl) { + ((TemplateImpl)obj).setDeactivated(!node.isChecked()); + } + } + + @Override + protected void installSpeedSearch() { + new TreeSpeedSearch(this, new Convertor() { + @Override + public String convert(TreePath o) { + Object object = ((DefaultMutableTreeNode)o.getLastPathComponent()).getUserObject(); + if (object instanceof TemplateGroup) { + return ((TemplateGroup)object).getName(); + } + if (object instanceof TemplateImpl) { + TemplateImpl template = (TemplateImpl)object; + return StringUtil.notNullize(template.getKey()) + + " " + + StringUtil.notNullize(template.getDescription()) + + " " + + template.getTemplateText(); + } + return ""; + } + }, true); + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateListPanel.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateListPanel.java index 24c80b3c12f7..217b74e424b6 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateListPanel.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateListPanel.java @@ -39,7 +39,6 @@ import com.intellij.util.NullableFunction; import com.intellij.util.ObjectUtils; import com.intellij.util.PlatformIcons; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.Convertor; import com.intellij.util.ui.tree.TreeUtil; import com.intellij.util.ui.update.UiNotifyConnector; import org.jetbrains.annotations.NotNull; @@ -454,15 +453,15 @@ public class TemplateListPanel extends JPanel implements Disposable { private JPanel createTable() { myTreeRoot = new CheckedTreeNode(null); - myTree = new CheckboxTree(new CheckboxTree.CheckboxTreeCellRenderer(){ + myTree = new LiveTemplateTree(new CheckboxTree.CheckboxTreeCellRenderer() { @Override public void customizeRenderer(final JTree tree, - Object value, - final boolean selected, - final boolean expanded, - final boolean leaf, - final int row, - final boolean hasFocus) { + Object value, + final boolean selected, + final boolean expanded, + final boolean leaf, + final int row, + final boolean hasFocus) { if (!(value instanceof DefaultMutableTreeNode)) return; value = ((DefaultMutableTreeNode)value).getUserObject(); @@ -473,42 +472,14 @@ public class TemplateListPanel extends JPanel implements Disposable { getTextRenderer().append(template.getKey(), new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, fgColor)); String description = template.getDescription(); if (StringUtil.isNotEmpty(description)) { - getTextRenderer().append (" (" + description + ")", SimpleTextAttributes.GRAY_ATTRIBUTES); + getTextRenderer().append(" (" + description + ")", SimpleTextAttributes.GRAY_ATTRIBUTES); } } else if (value instanceof TemplateGroup) { - getTextRenderer().append (((TemplateGroup)value).getName(), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); - } - - } - }, myTreeRoot) { - @Override - protected void onNodeStateChanged(final CheckedTreeNode node) { - Object obj = node.getUserObject(); - if (obj instanceof TemplateImpl) { - ((TemplateImpl)obj).setDeactivated(!node.isChecked()); + getTextRenderer().append(((TemplateGroup)value).getName(), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); } } - - @Override - protected void installSpeedSearch() { - new TreeSpeedSearch(this, new Convertor() { - @Override - public String convert(TreePath o) { - Object object = ((DefaultMutableTreeNode)o.getLastPathComponent()).getUserObject(); - if (object instanceof TemplateGroup) { - return ((TemplateGroup)object).getName(); - } - if (object instanceof TemplateImpl) { - TemplateImpl template = (TemplateImpl)object; - return StringUtil.notNullize(template.getKey()) + " " + StringUtil.notNullize(template.getDescription()) + " " + template.getTemplateText(); - } - return ""; - } - }, true); - - } - }; + }, myTreeRoot); myTree.setRootVisible(false); myTree.setShowsRootHandles(true); myTree.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION); From 8f48f290c2d4a02cd7a37d569e7185804ffc4b35 Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 2 Jul 2015 17:39:31 +0200 Subject: [PATCH 114/137] enable cut & paste actions in modal context (copy is enabled already) --- .../platform-impl/src/com/intellij/ide/actions/CutAction.java | 4 ++++ .../src/com/intellij/ide/actions/PasteAction.java | 3 +++ 2 files changed, 7 insertions(+) diff --git a/platform/platform-impl/src/com/intellij/ide/actions/CutAction.java b/platform/platform-impl/src/com/intellij/ide/actions/CutAction.java index 4b92484ce894..8088f48209cc 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/CutAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/CutAction.java @@ -21,6 +21,10 @@ import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; public class CutAction extends AnAction implements DumbAware { + public CutAction() { + setEnabledInModalContext(true); + } + public void actionPerformed(AnActionEvent e) { DataContext dataContext = e.getDataContext(); CutProvider provider = PlatformDataKeys.CUT_PROVIDER.getData(dataContext); diff --git a/platform/platform-impl/src/com/intellij/ide/actions/PasteAction.java b/platform/platform-impl/src/com/intellij/ide/actions/PasteAction.java index 74e4ac38abb8..acb0cf3c6896 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/PasteAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/PasteAction.java @@ -20,6 +20,9 @@ import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.project.DumbAware; public class PasteAction extends AnAction implements DumbAware { + public PasteAction() { + setEnabledInModalContext(true); + } public void update(AnActionEvent event) { Presentation presentation = event.getPresentation(); From 2a35b9a9ddef8dd1c1c16105fc6f9cf90c275d66 Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 2 Jul 2015 17:41:47 +0200 Subject: [PATCH 115/137] IDEA-141077 Allow easy live template sharing between different IDEA instances --- .../template/impl/LiveTemplateTree.java | 99 ++++++++++++++++++- .../template/impl/TemplateListPanel.java | 19 ++-- .../template/impl/TemplateSettings.java | 39 ++++---- 3 files changed, 128 insertions(+), 29 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/LiveTemplateTree.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/LiveTemplateTree.java index 150b87bc6056..29f835193636 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/LiveTemplateTree.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/LiveTemplateTree.java @@ -15,21 +15,45 @@ */ package com.intellij.codeInsight.template.impl; +import com.intellij.codeInsight.template.TemplateContextType; +import com.intellij.ide.CopyProvider; +import com.intellij.ide.PasteProvider; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.DataProvider; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.ide.CopyPasteManager; +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.CheckboxTree; import com.intellij.ui.CheckedTreeNode; import com.intellij.ui.TreeSpeedSearch; +import com.intellij.util.Function; +import com.intellij.util.SystemProperties; import com.intellij.util.containers.Convertor; +import org.jdom.Element; +import org.jdom.JDOMException; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreePath; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.io.IOException; +import java.io.StringReader; +import java.util.Set; /** * @author peter */ -class LiveTemplateTree extends CheckboxTree { - LiveTemplateTree(final CheckboxTreeCellRenderer renderer, final CheckedTreeNode root) { +class LiveTemplateTree extends CheckboxTree implements DataProvider, CopyProvider, PasteProvider { + private final TemplateListPanel myConfigurable; + + LiveTemplateTree(final CheckboxTreeCellRenderer renderer, final CheckedTreeNode root, TemplateListPanel configurable) { super(renderer, root); + myConfigurable = configurable; } @Override @@ -61,4 +85,75 @@ class LiveTemplateTree extends CheckboxTree { } }, true); } + + @Nullable + @Override + public Object getData(@NonNls String dataId) { + if (PlatformDataKeys.COPY_PROVIDER.is(dataId) || PlatformDataKeys.PASTE_PROVIDER.is(dataId)) { + return this; + } + return null; + } + + @Override + public void performCopy(@NotNull DataContext dataContext) { + final Set templates = myConfigurable.getSelectedTemplates().keySet(); + + + CopyPasteManager.getInstance().setContents(new StringSelection(StringUtil.join(templates, new Function() { + @Override + public String fun(TemplateImpl template) { + TemplateContext zeroContext = new TemplateContext(); + for (TemplateContextType type : TemplateContextType.EP_NAME.getExtensions()) { + zeroContext.setEnabled(type, false); + } + return JDOMUtil.writeElement(TemplateSettings.serializeTemplate(template, zeroContext)); + } + }, SystemProperties.getLineSeparator()))); + + } + + @Override + public boolean isCopyEnabled(@NotNull DataContext dataContext) { + return !myConfigurable.getSelectedTemplates().isEmpty(); + } + + @Override + public boolean isCopyVisible(@NotNull DataContext dataContext) { + return isCopyEnabled(dataContext); + } + + @Override + public boolean isPastePossible(@NotNull DataContext dataContext) { + if (myConfigurable.getSingleSelectedGroup() == null) return false; + + String s = CopyPasteManager.getInstance().getContents(DataFlavor.stringFlavor); + return s != null && s.startsWith("