diff --git a/java/debugger/impl/src/com/intellij/debugger/DefaultDebugUIEnvironment.java b/java/debugger/impl/src/com/intellij/debugger/DefaultDebugUIEnvironment.java index 6f8b13e4d06a..c88e36d62c3c 100644 --- a/java/debugger/impl/src/com/intellij/debugger/DefaultDebugUIEnvironment.java +++ b/java/debugger/impl/src/com/intellij/debugger/DefaultDebugUIEnvironment.java @@ -20,12 +20,13 @@ import com.intellij.execution.configurations.RemoteConnection; import com.intellij.execution.configurations.RunProfile; import com.intellij.execution.configurations.RunProfileState; import com.intellij.execution.runners.ExecutionEnvironment; -import com.intellij.execution.runners.RestartAction; import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.execution.ui.actions.CloseAction; import com.intellij.ide.actions.ContextHelpAction; +import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.Constraints; import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.actionSystem.IdeActions; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -62,9 +63,7 @@ public class DefaultDebugUIEnvironment implements DebugUIEnvironment { @Override public void initActions(RunContentDescriptor content, DefaultActionGroup actionGroup) { Executor executor = myExecutionEnvironment.getExecutor(); - RestartAction restartAction = new RestartAction(content, myExecutionEnvironment); - actionGroup.add(restartAction, Constraints.FIRST); - restartAction.registerShortcut(content.getComponent()); + actionGroup.add(ActionManager.getInstance().getAction(IdeActions.ACTION_RERUN), Constraints.FIRST); actionGroup.add(new CloseAction(executor, content, myExecutionEnvironment.getProject())); actionGroup.add(new ContextHelpAction(executor.getHelpId())); 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 c6c20ff587e8..44337a6f1ee3 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java @@ -28,6 +28,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.NullableComputable; import com.intellij.openapi.util.Ref; 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; @@ -139,6 +140,19 @@ public class PositionManagerImpl implements PositionManager { lineNumber = -1; } + if (psiFile instanceof PsiCompiledElement && lineNumber > -1) { + VirtualFile file = psiFile.getVirtualFile(); + if (file != null) { + int[] data = file.getUserData(LINE_NUMBERS_MAPPING_KEY); + if (data != null) { + int line = mapLine(lineNumber+1, data); + if (line > -1) { + return SourcePosition.createFromLine(psiFile, line-1); + } + } + } + } + if (psiFile instanceof PsiCompiledElement || lineNumber < 0) { final String methodSignature = location.method().signature(); if (methodSignature == null) { @@ -165,6 +179,15 @@ public class PositionManagerImpl implements PositionManager { return SourcePosition.createFromLine(psiFile, lineNumber); } + private static int mapLine(int line, int[] mapping) { + for (int i = 0; i < mapping.length; i+=2) { + if (mapping[i] == line) { + return mapping[i+1]; + } + } + return -1; + } + @Nullable private PsiFile getPsiFileByLocation(final Project project, final Location location) { if (location == null) { diff --git a/java/idea-ui/src/com/intellij/ide/actions/ImportModuleAction.java b/java/idea-ui/src/com/intellij/ide/actions/ImportModuleAction.java index 3f087a998304..76e356780b83 100644 --- a/java/idea-ui/src/com/intellij/ide/actions/ImportModuleAction.java +++ b/java/idea-ui/src/com/intellij/ide/actions/ImportModuleAction.java @@ -136,7 +136,7 @@ public class ImportModuleAction extends AnAction { }); StringBuilder builder = new StringBuilder("Select "); boolean first = true; - if (list.size() > 1) { + if (list.size() > 0) { for (ProjectImportProvider provider : list) { String sample = provider.getFileSample(); if (sample != null) { diff --git a/java/java-tests/testData/projectView/foldersOnTop/a.java b/java/java-tests/testData/projectView/foldersOnTop/a.java new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/projectView/foldersOnTop/a.txt b/java/java-tests/testData/projectView/foldersOnTop/a.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/projectView/foldersOnTop/b.java/dummy.txt b/java/java-tests/testData/projectView/foldersOnTop/b.java/dummy.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/projectView/foldersOnTop/b.txt/dummy.txt b/java/java-tests/testData/projectView/foldersOnTop/b.txt/dummy.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/projectView/foldersOnTop/c.java b/java/java-tests/testData/projectView/foldersOnTop/c.java new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/projectView/foldersOnTop/c.txt b/java/java-tests/testData/projectView/foldersOnTop/c.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/projectView/sortByName/a-a.java b/java/java-tests/testData/projectView/sortByName/a-a.java new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/projectView/sortByName/a-b.java b/java/java-tests/testData/projectView/sortByName/a-b.java new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/projectView/sortByName/a.java b/java/java-tests/testData/projectView/sortByName/a.java new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/projectView/sortByName/ab.java b/java/java-tests/testData/projectView/sortByName/ab.java new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/projectView/sortByName/b.java b/java/java-tests/testData/projectView/sortByName/b.java new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/projectView/sortByType/a.java b/java/java-tests/testData/projectView/sortByType/a.java new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/projectView/sortByType/a.txt b/java/java-tests/testData/projectView/sortByType/a.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/projectView/sortByType/b.java b/java/java-tests/testData/projectView/sortByType/b.java new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/projectView/sortByType/b.txt b/java/java-tests/testData/projectView/sortByType/b.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/projectView/sortByTypeBetweenFilesAndFolders/a.java b/java/java-tests/testData/projectView/sortByTypeBetweenFilesAndFolders/a.java new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/projectView/sortByTypeBetweenFilesAndFolders/a.java_folder/dummy.txt b/java/java-tests/testData/projectView/sortByTypeBetweenFilesAndFolders/a.java_folder/dummy.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/projectView/sortByTypeBetweenFilesAndFolders/a.txt b/java/java-tests/testData/projectView/sortByTypeBetweenFilesAndFolders/a.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/projectView/sortByTypeBetweenFilesAndFolders/a_folder/dummy.txt b/java/java-tests/testData/projectView/sortByTypeBetweenFilesAndFolders/a_folder/dummy.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/projectView/sortByTypeBetweenFilesAndFolders/b.java b/java/java-tests/testData/projectView/sortByTypeBetweenFilesAndFolders/b.java new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/projectView/sortByTypeBetweenFilesAndFolders/b.java_folder/dummy.txt b/java/java-tests/testData/projectView/sortByTypeBetweenFilesAndFolders/b.java_folder/dummy.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/projectView/sortByTypeBetweenFilesAndFolders/b.txt b/java/java-tests/testData/projectView/sortByTypeBetweenFilesAndFolders/b.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/projectView/sortByTypeBetweenFilesAndFolders/b_folder/dummy.txt b/java/java-tests/testData/projectView/sortByTypeBetweenFilesAndFolders/b_folder/dummy.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testSrc/com/intellij/projectView/ProjectTreeSortingTest.java b/java/java-tests/testSrc/com/intellij/projectView/ProjectTreeSortingTest.java new file mode 100644 index 000000000000..1307dabc884f --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/projectView/ProjectTreeSortingTest.java @@ -0,0 +1,159 @@ +/* + * Copyright 2000-2014 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.projectView; + +import com.intellij.ide.projectView.ProjectView; +import com.intellij.ide.projectView.impl.AbstractProjectViewPSIPane; +import com.intellij.ide.projectView.impl.ProjectViewImpl; +import com.intellij.openapi.ui.Queryable; +import com.intellij.openapi.util.Disposer; +import com.intellij.testFramework.PlatformTestUtil; +import com.intellij.util.ui.tree.TreeUtil; + +import javax.swing.tree.DefaultMutableTreeNode; + +public class ProjectTreeSortingTest extends BaseProjectViewTestCase { + private ProjectView myProjectView; + private AbstractProjectViewPSIPane myPane; + private boolean myOriginalSortByType; + private boolean myOriginalFoldersAlwaysOnTop; + + @Override + public void setUp() throws Exception { + super.setUp(); + + myPane = new TestProjectViewPSIPane(myProject, myStructure, 9); + myPane.createComponent(); + Disposer.register(myStructure, myPane); + + myProjectView = ProjectView.getInstance(myProject); + myProjectView.addProjectPane(myPane); + myOriginalSortByType = myProjectView.isSortByType(myPane.getId()); + myOriginalFoldersAlwaysOnTop = ((ProjectViewImpl)myProjectView).isFoldersAlwaysOnTop(); + + TreeUtil.expand(myPane.getTree(), 2); + } + + @Override + public void tearDown() throws Exception { + myProjectView.setSortByType(myPane.getId(), myOriginalSortByType); + ((ProjectViewImpl)myProjectView).setFoldersAlwaysOnTop(myOriginalFoldersAlwaysOnTop); + myProjectView.removeProjectPane(myPane); + + super.tearDown(); + } + + public void testSortByName() throws Exception { + myProjectView.setSortByType(myPane.getId(), false); + assertTree("-sortByName\n" + + " a.java\n" + + " a-a.java\n" + + " a-b.java\n" + + " ab.java\n" + + " b.java\n"); + } + + public void testSortByType() throws Exception { + myProjectView.setSortByType(myPane.getId(), false); + assertTree("-sortByType\n" + + " a.java\n" + + " a.txt\n" + + " b.java\n" + + " b.txt\n"); + + myProjectView.setSortByType(myPane.getId(), true); + assertTree("-sortByType\n" + + " a.java\n" + + " b.java\n" + + " a.txt\n" + + " b.txt\n"); + } + + public void testFoldersOnTop() throws Exception { + // first, check with 'sort by type' disabled + myProjectView.setSortByType(myPane.getId(), false); + + ((ProjectViewImpl)myProjectView).setFoldersAlwaysOnTop(true); + assertTree("-foldersOnTop\n" + + " +b.java\n" + + " +b.txt\n" + + " a.java\n" + + " a.txt\n" + + " c.java\n" + + " c.txt\n"); + + ((ProjectViewImpl)myProjectView).setFoldersAlwaysOnTop(false); + assertTree("-foldersOnTop\n" + + " a.java\n" + + " a.txt\n" + + " +b.java\n" + + " +b.txt\n" + + " c.java\n" + + " c.txt\n"); + + // now let's check the behavior, when sortByType is enabled + myProjectView.setSortByType(myPane.getId(), true); + + ((ProjectViewImpl)myProjectView).setFoldersAlwaysOnTop(true); + assertTree("-foldersOnTop\n" + + " +b.java\n" + + " +b.txt\n" + + " a.java\n" + + " c.java\n" + + " a.txt\n" + + " c.txt\n"); + + ((ProjectViewImpl)myProjectView).setFoldersAlwaysOnTop(false); + assertTree("-foldersOnTop\n" + + " a.java\n" + + " c.java\n" + + " a.txt\n" + + " c.txt\n" + + " +b.java\n" + + " +b.txt\n"); + } + + public void testSortByTypeBetweenFilesAndFolders() throws Exception { + ((ProjectViewImpl)myProjectView).setFoldersAlwaysOnTop(false); + myProjectView.setSortByType(myPane.getId(), false); + assertTree("-sortByTypeBetweenFilesAndFolders\n" + + " a.java\n" + + " +a.java_folder\n" + + " a.txt\n" + + " +a_folder\n" + + " b.java\n" + + " +b.java_folder\n" + + " b.txt\n" + + " +b_folder\n"); + + myProjectView.setSortByType(myPane.getId(), true); + assertTree("-sortByTypeBetweenFilesAndFolders\n" + + " a.java\n" + + " b.java\n" + + " a.txt\n" + + " b.txt\n" + + " +a.java_folder\n" + + " +a_folder\n" + + " +b.java_folder\n" + + " +b_folder\n"); + } + + private void assertTree(String expected) { + DefaultMutableTreeNode element = myPane.getTreeBuilder().getNodeForElement(getContentDirectory()); + assertNotNull("Element for " + getContentDirectory() + " not found", element); + assertEquals(expected, PlatformTestUtil.print(myPane.getTree(), element, new Queryable.PrintInfo(), false)); + } +} diff --git a/platform/bootstrap/src/com/intellij/idea/Main.java b/platform/bootstrap/src/com/intellij/idea/Main.java index ce8a2d0f14e9..4b805923cb6b 100644 --- a/platform/bootstrap/src/com/intellij/idea/Main.java +++ b/platform/bootstrap/src/com/intellij/idea/Main.java @@ -129,16 +129,28 @@ public class Main { // always delete previous patch copy File patchCopy = new File(tempDir, patchFileName + "_copy"); File log4jCopy = new File(tempDir, "log4j.jar." + platform + "_copy"); - if (!FileUtilRt.delete(patchCopy) || !FileUtilRt.delete(log4jCopy)) { + File jnaUtilsCopy = new File(tempDir, "jna-utils.jar." + platform + "_copy"); + File jnaCopy = new File(tempDir, "jna.jar." + platform + "_copy"); + if (!FileUtilRt.delete(patchCopy) || !FileUtilRt.delete(log4jCopy) || !FileUtilRt.delete(jnaUtilsCopy) || !FileUtilRt.delete(jnaCopy)) { throw new IOException("Cannot delete temporary files in " + tempDir); } File patch = new File(tempDir, patchFileName); if (!patch.exists()) return; + File log4j = new File(PathManager.getLibPath(), "log4j.jar"); - if (!log4j.exists()) throw new IOException("Log4J missing: " + log4j); + if (!log4j.exists()) throw new IOException("Log4J is missing: " + log4j); + + File jnaUtils = new File(PathManager.getLibPath(), "jna-utils.jar"); + if (!jnaUtils.exists()) throw new IOException("jna-utils.jar is missing: " + jnaUtils); + + File jna = new File(PathManager.getLibPath(), "jna.jar"); + if (!jna.exists()) throw new IOException("jna is missing: " + jna); + copyFile(patch, patchCopy, true); copyFile(log4j, log4jCopy, false); + copyFile(jna, jnaCopy, false); + copyFile(jnaUtils, jnaUtilsCopy, false); int status = 0; if (Restarter.isSupported()) { @@ -154,7 +166,7 @@ public class Main { System.getProperty("java.home") + "/bin/java", "-Xmx500m", "-classpath", - patchCopy.getPath() + File.pathSeparator + log4jCopy.getPath(), + patchCopy.getPath() + File.pathSeparator + log4jCopy.getPath() + File.pathSeparator + jnaCopy.getPath() + File.pathSeparator + jnaUtilsCopy.getPath(), "-Djava.io.tmpdir=" + tempDir, "-Didea.updater.log=" + PathManager.getLogPath(), "-Dswing.defaultlaf=" + UIManager.getSystemLookAndFeelClassName(), diff --git a/platform/core-api/src/com/intellij/psi/util/PsiUtilCore.java b/platform/core-api/src/com/intellij/psi/util/PsiUtilCore.java index bc93aa3033d8..79b1514c7211 100644 --- a/platform/core-api/src/com/intellij/psi/util/PsiUtilCore.java +++ b/platform/core-api/src/com/intellij/psi/util/PsiUtilCore.java @@ -31,7 +31,9 @@ import com.intellij.psi.meta.PsiMetaOwner; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.SearchScope; +import com.intellij.psi.tree.IElementType; import com.intellij.util.TimeoutUtil; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -536,4 +538,14 @@ public class PsiUtilCore { } }); } + + @Contract("null -> null;!null -> !null") + public static IElementType getElementType(@Nullable ASTNode node) { + return node == null ? null : node.getElementType(); + } + + @Contract("null -> null;!null -> !null") + public static IElementType getElementType(@Nullable PsiElement element) { + return element == null ? null : getElementType(element.getNode()); + } } diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/CommitNode.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/CommitNode.java index 666db56ca331..4e2701ad6ac0 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/CommitNode.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/CommitNode.java @@ -18,6 +18,7 @@ package com.intellij.dvcs.push.ui; import com.intellij.dvcs.DvcsUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.changes.issueLinks.IssueLinkHtmlRenderer; +import com.intellij.openapi.vcs.changes.issueLinks.IssueLinkRenderer; import com.intellij.ui.ColoredTreeCellRenderer; import com.intellij.ui.SimpleTextAttributes; import com.intellij.vcs.log.VcsFullCommitDetails; @@ -41,8 +42,9 @@ public class CommitNode extends DefaultMutableTreeNode implements CustomRendered @Override public void render(@NotNull ColoredTreeCellRenderer renderer) { - String subject = getUserObject().getSubject(); - renderer.append(subject, SimpleTextAttributes.REGULAR_ATTRIBUTES); + new IssueLinkRenderer(myProject, renderer).appendTextWithLinks(getUserObject().getSubject(), + new SimpleTextAttributes(SimpleTextAttributes.STYLE_SMALLER, + renderer.getForeground())); } public String getTooltip() { diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/VcsBranchEditorListener.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/VcsBranchEditorListener.java index 54a5af2dae63..43b7c1d4e1f4 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/VcsBranchEditorListener.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/VcsBranchEditorListener.java @@ -15,13 +15,11 @@ */ package com.intellij.dvcs.push.ui; -import com.intellij.dvcs.push.PushTargetPanel; import com.intellij.openapi.vcs.changes.issueLinks.LinkMouseListenerBase; import com.intellij.ui.CheckboxTree; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.awt.*; import java.awt.event.MouseEvent; public class VcsBranchEditorListener extends LinkMouseListenerBase { @@ -31,18 +29,6 @@ public class VcsBranchEditorListener extends LinkMouseListenerBase { myRenderer = renderer; } - @Override - public void mouseMoved(MouseEvent e) { - Component component = (Component)e.getSource(); - Object tag = getTagAt(e); - if (tag instanceof PushTargetPanel || tag instanceof TextWithLinkNode || tag instanceof ExtraEditControl) { - component.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - } - else { - component.setCursor(Cursor.getDefaultCursor()); - } - } - @Nullable @Override protected Object getTagAt(@NotNull final MouseEvent e) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java index 88d55b64eb0e..850e62564dc3 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java @@ -512,7 +512,7 @@ public class CommentByLineCommentHandler extends MultiCaretCodeInsightActionHand if (startOffset == endOffset) { return; } - RangeMarker marker = block.editor.getDocument().createRangeMarker(startOffset, endOffset); + RangeMarker marker = endOffset > startOffset ? block.editor.getDocument().createRangeMarker(startOffset, endOffset) : null; String prefix = commenter.getLineCommentPrefix(); if (prefix != null) { CharSequence chars = document.getCharsSequence(); @@ -581,7 +581,9 @@ public class CommentByLineCommentHandler extends MultiCaretCodeInsightActionHand for (int i = prefixes.size() - 1; i >= 0; i--) { uncommentRange(document, startOffset + prefixes.get(i), Math.min(startOffset + suffixes.get(i) + suffix.length(), endOffset), commenter); } - CommentByBlockCommentHandler.processDocument(document, marker, commenter, false); + if (marker != null) { + CommentByBlockCommentHandler.processDocument(document, marker, commenter, false); + } } private static void commentLine(Block block, int line, int offset) { diff --git a/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java b/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java index 9aab415d5e70..efb56f449d26 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java +++ b/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java @@ -56,6 +56,7 @@ import org.jetbrains.annotations.Nullable; import org.xmlpull.v1.XmlPullParserFactory; import org.xmlpull.v1.XmlSerializer; +import javax.swing.*; import java.awt.event.KeyEvent; import java.io.*; import java.util.List; @@ -75,6 +76,7 @@ public class ConsoleHistoryController { private final AnAction myHistoryPrev = new MyAction(false); private final AnAction myBrowseHistory = new MyBrowseAction(); private boolean myMultiline; + private boolean isStandardUpDownUsed = false; private final ModelHelper myHelper; private long myLastSaveStamp; @@ -115,6 +117,7 @@ public class ConsoleHistoryController { loadHistory(myHelper.getId()); } configureActions(); + isStandardUpDownUsed = checkIfStandardUpDownUsed(); myLastSaveStamp = getCurrentTimeStamp(); } @@ -122,21 +125,18 @@ public class ConsoleHistoryController { return getModel().getModificationCount() + myConsole.getEditorDocument().getModificationStamp(); } - private void configureActions() { + private boolean checkIfStandardUpDownUsed() { + return isShortcutSetsIntersect(myHistoryNext.getShortcutSet(), getShortcutUpDown(true)) + || isShortcutSetsIntersect(myHistoryPrev.getShortcutSet(), getShortcutUpDown(false)); + } + + protected void configureActions() { EmptyAction.setupAction(myHistoryNext, "Console.History.Next", null); EmptyAction.setupAction(myHistoryPrev, "Console.History.Previous", null); EmptyAction.setupAction(myBrowseHistory, "Console.History.Browse", null); if (!myMultiline) { - AnAction up = ActionManager.getInstance().getActionOrStub(IdeActions.ACTION_EDITOR_MOVE_CARET_UP); - AnAction down = ActionManager.getInstance().getActionOrStub(IdeActions.ACTION_EDITOR_MOVE_CARET_DOWN); - if (up != null && down != null) { - myHistoryNext.registerCustomShortcutSet(up.getShortcutSet(), null); - myHistoryPrev.registerCustomShortcutSet(down.getShortcutSet(), null); - } - else { - myHistoryNext.registerCustomShortcutSet(KeyEvent.VK_UP, 0, null); - myHistoryPrev.registerCustomShortcutSet(KeyEvent.VK_DOWN, 0, null); - } + myHistoryNext.registerCustomShortcutSet(getShortcutUpDown(true), null); + myHistoryPrev.registerCustomShortcutSet(getShortcutUpDown(false), null); } myHistoryNext.registerCustomShortcutSet(myHistoryNext.getShortcutSet(), myConsole.getCurrentEditor().getComponent()); myHistoryPrev.registerCustomShortcutSet(myHistoryPrev.getShortcutSet(), myConsole.getCurrentEditor().getComponent()); @@ -263,7 +263,7 @@ public class ConsoleHistoryController { @Override public void update(final AnActionEvent e) { super.update(e); - e.getPresentation().setEnabled(myMultiline || canMoveInEditor(myNext)); + e.getPresentation().setEnabled(!isStandardUpDownUsed || canMoveInEditor(myNext)); } } @@ -505,4 +505,29 @@ public class ConsoleHistoryController { out.endTag(null, tag); } } + + private static ShortcutSet getShortcutUpDown(boolean isUp) { + AnAction action = ActionManager.getInstance().getActionOrStub(isUp ? + IdeActions.ACTION_EDITOR_MOVE_CARET_UP : + IdeActions.ACTION_EDITOR_MOVE_CARET_DOWN); + if (action != null) { + return action.getShortcutSet(); + } + return new CustomShortcutSet(KeyStroke.getKeyStroke(isUp ? KeyEvent.VK_UP : KeyEvent.VK_DOWN, 0)); + } + + private static boolean isShortcutSetsIntersect(ShortcutSet set1, ShortcutSet set2) { + final Shortcut[] shortcuts1 = set1.getShortcuts(); + final Shortcut[] shortcuts2 = set2.getShortcuts(); + + for (Shortcut s1 : shortcuts1) { + for (Shortcut s2 : shortcuts2) { + if (s1.equals(s2)) { + return true; + } + } + } + return false; + } + } diff --git a/platform/lang-impl/src/com/intellij/execution/runners/AbstractConsoleRunnerWithHistory.java b/platform/lang-impl/src/com/intellij/execution/runners/AbstractConsoleRunnerWithHistory.java index d1c3c4877493..517a4cab42b2 100644 --- a/platform/lang-impl/src/com/intellij/execution/runners/AbstractConsoleRunnerWithHistory.java +++ b/platform/lang-impl/src/com/intellij/execution/runners/AbstractConsoleRunnerWithHistory.java @@ -253,31 +253,8 @@ public abstract class AbstractConsoleRunnerWithHistory registry = ContainerUtil.createLockFreeCopyOnWriteList(); - @Override - public void update(AnActionEvent event) { + public void update(@NotNull AnActionEvent event) { Presentation presentation = event.getPresentation(); ExecutionEnvironment environment = getEnvironment(event); if (environment != null) { @@ -55,22 +41,14 @@ class FakeRerunAction extends AnAction implements DumbAware { return; } - FakeRerunAction action = findActualAction(event); - presentation.setEnabled(action != null && action.isEnabled(event)); - presentation.setVisible(false); + presentation.setEnabledAndVisible(false); } @Override - public void actionPerformed(AnActionEvent event) { + public void actionPerformed(@NotNull AnActionEvent event) { ExecutionEnvironment environment = getEnvironment(event); if (environment != null) { ExecutionUtil.restart(environment); - return; - } - - FakeRerunAction action = findActualAction(event); - if (action != null && action.isEnabled(event)) { - action.actionPerformed(event); } } @@ -92,50 +70,4 @@ class FakeRerunAction extends AnAction implements DumbAware { !ExecutorRegistry.getInstance().isStarting(environment) && !(processHandler != null && processHandler.isProcessTerminating()); } - - @Nullable - private JComponent getRunComponent(@NotNull AnActionEvent event) { - RunContentDescriptor descriptor = getDescriptor(event); - return descriptor == null ? null : descriptor.getComponent(); - } - - @Nullable - private static FakeRerunAction findActualAction(@NotNull final AnActionEvent event) { - if (registry.isEmpty()) { - return null; - } - - List candidates = new ArrayList(registry); - Collections.sort(candidates, new Comparator() { - @Override - public int compare(@NotNull FakeRerunAction action1, @NotNull FakeRerunAction action2) { - boolean isActive1 = action1.isEnabled(event); - if (isActive1 != action2.isEnabled(event)) { - return isActive1 ? -1 : 1; - } - - JComponent component1 = action1.getRunComponent(event); - JComponent component2 = action2.getRunComponent(event); - Window window1 = component1 == null ? null : SwingUtilities.windowForComponent(component1); - Window window2 = component2 == null ? null : SwingUtilities.windowForComponent(component2); - if (window1 == null) { - return 1; - } - if (window2 == null) { - return -1; - } - - boolean showing1 = component1.isShowing(); - boolean showing2 = component2.isShowing(); - if (showing1 && !showing2) { - return -1; - } - if (showing2 && !showing1) { - return 1; - } - return (window1.isActive() ? -1 : 1); - } - }); - return candidates.get(0); - } } diff --git a/platform/lang-impl/src/com/intellij/execution/runners/RestartAction.java b/platform/lang-impl/src/com/intellij/execution/runners/RestartAction.java deleted file mode 100644 index 1ab3cee3c8cb..000000000000 --- a/platform/lang-impl/src/com/intellij/execution/runners/RestartAction.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2000-2014 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.execution.runners; - -import com.intellij.execution.Executor; -import com.intellij.execution.ui.RunContentDescriptor; -import com.intellij.openapi.Disposable; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CustomShortcutSet; -import com.intellij.openapi.actionSystem.IdeActions; -import com.intellij.openapi.keymap.KeymapManager; -import com.intellij.openapi.project.DumbAware; -import com.intellij.openapi.util.Disposer; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; - -@Deprecated -/** - * to remove in IDEA 15 - */ -public class RestartAction extends FakeRerunAction implements DumbAware, AnAction.TransparentUpdate, Disposable { - private final RunContentDescriptor myDescriptor; - private final ExecutionEnvironment myEnvironment; - - public RestartAction(@NotNull RunContentDescriptor descriptor, @NotNull ExecutionEnvironment environment) { - //noinspection deprecation - this(environment.getExecutor(), null, descriptor, environment); - } - - @Deprecated - /** - * @deprecated environment must provide runner id - * to remove in IDEA 15 - */ - public RestartAction(@SuppressWarnings("UnusedParameters") @NotNull Executor executor, - @Nullable ProgramRunner runner, - @NotNull RunContentDescriptor descriptor, - @NotNull ExecutionEnvironment environment) { - Disposer.register(descriptor, this); - FakeRerunAction.registry.add(this); - - myEnvironment = runner == null ? environment : RunContentBuilder.fix(environment, runner); - getTemplatePresentation().setEnabled(false); - myDescriptor = descriptor; - } - - @Override - public void dispose() { - FakeRerunAction.registry.remove(this); - } - - @Override - @NotNull - protected RunContentDescriptor getDescriptor(AnActionEvent event) { - return myDescriptor; - } - - @Override - @NotNull - protected ExecutionEnvironment getEnvironment(AnActionEvent event) { - return myEnvironment; - } - - public void registerShortcut(JComponent component) { - registerCustomShortcutSet(new CustomShortcutSet(KeymapManager.getInstance().getActiveKeymap().getShortcuts(IdeActions.ACTION_RERUN)), - component); - } -} diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/GroupByTypeComparator.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/GroupByTypeComparator.java index 10131d29cfaf..4abc7af79073 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/GroupByTypeComparator.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/GroupByTypeComparator.java @@ -63,9 +63,12 @@ public class GroupByTypeComparator implements Comparator { if (descriptor1 instanceof ProjectViewNode && descriptor2 instanceof ProjectViewNode) { final Project project = descriptor1.getProject(); final ProjectView projectView = ProjectView.getInstance(project); - if (!(projectView instanceof ProjectViewImpl && !((ProjectViewImpl)projectView).isFoldersAlwaysOnTop())) { - ProjectViewNode node1 = (ProjectViewNode)descriptor1; - ProjectViewNode node2 = (ProjectViewNode)descriptor2; + + ProjectViewNode node1 = (ProjectViewNode)descriptor1; + ProjectViewNode node2 = (ProjectViewNode)descriptor2; + + boolean isFoldersOnTop = !(projectView instanceof ProjectViewImpl && !((ProjectViewImpl)projectView).isFoldersAlwaysOnTop()); + if (isFoldersOnTop) { int typeWeight1 = node1.getTypeSortWeight(isSortByType()); int typeWeight2 = node2.getTypeSortWeight(isSortByType()); if (typeWeight1 != 0 && typeWeight2 == 0) { @@ -77,30 +80,34 @@ public class GroupByTypeComparator implements Comparator { if (typeWeight1 != 0 && typeWeight2 != typeWeight1) { return typeWeight1 - typeWeight2; } - - if (isSortByType()) { - final Comparable typeSortKey1 = node1.getTypeSortKey(); - final Comparable typeSortKey2 = node2.getTypeSortKey(); - if (typeSortKey1 != null && typeSortKey2 != null) { - final int result = typeSortKey1.compareTo(typeSortKey2); - if (result != 0) return result; - } + } + + if (isSortByType()) { + final Comparable typeSortKey1 = node1.getTypeSortKey(); + final Comparable typeSortKey2 = node2.getTypeSortKey(); + if (!(typeSortKey1 == null && typeSortKey2 == null)) { + if (typeSortKey1 == null) return 1; + if (typeSortKey2 == null) return -1; + //noinspection unchecked + final int result = typeSortKey1.compareTo(typeSortKey2); + if (result != 0) return result; } - else { - final Comparable typeSortKey1 = node1.getSortKey(); - final Comparable typeSortKey2 = node2.getSortKey(); - if (typeSortKey1 != null && typeSortKey2 != null) { - final int result = typeSortKey1.compareTo(typeSortKey2); - if (result != 0) return result; - } + } + else { + final Comparable typeSortKey1 = node1.getSortKey(); + final Comparable typeSortKey2 = node2.getSortKey(); + if (typeSortKey1 != null && typeSortKey2 != null) { + //noinspection unchecked + final int result = typeSortKey1.compareTo(typeSortKey2); + if (result != 0) return result; } + } - if (isAbbreviateQualifiedNames()) { - String key1 = node1.getQualifiedNameSortKey(); - String key2 = node2.getQualifiedNameSortKey(); - if (key1 != null && key2 != null) { - return key1.compareToIgnoreCase(key2); - } + if (isAbbreviateQualifiedNames()) { + String key1 = node1.getQualifiedNameSortKey(); + String key2 = node2.getQualifiedNameSortKey(); + if (key1 != null && key2 != null) { + return key1.compareToIgnoreCase(key2); } } } @@ -119,5 +126,4 @@ public class GroupByTypeComparator implements Comparator { private boolean isAbbreviateQualifiedNames() { return myProjectView != null && myProjectView.isAbbreviatePackageNames(myPaneId); } - } diff --git a/platform/lang-impl/src/com/intellij/ide/util/scopeChooser/ScopeEditorForm.form b/platform/lang-impl/src/com/intellij/ide/util/scopeChooser/ScopeEditorForm.form index 0106eceb38ba..c4719d0ff9b0 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/scopeChooser/ScopeEditorForm.form +++ b/platform/lang-impl/src/com/intellij/ide/util/scopeChooser/ScopeEditorForm.form @@ -125,7 +125,7 @@ - + diff --git a/platform/platform-api/src/com/intellij/ui/RelativeFont.java b/platform/platform-api/src/com/intellij/ui/RelativeFont.java index cd45e51dec70..2bc8f5e3567c 100644 --- a/platform/platform-api/src/com/intellij/ui/RelativeFont.java +++ b/platform/platform-api/src/com/intellij/ui/RelativeFont.java @@ -24,30 +24,27 @@ import javax.swing.plaf.FontUIResource; /** * @author Sergey.Malenkov */ -public final class RelativeFont implements PropertyChangeListener { - private static final String PROPERTY = "font"; +public enum RelativeFont implements PropertyChangeListener { + PLAIN(Font.PLAIN, 0), + BOLD(Font.BOLD, 0), + LARGE(1f), + SMALL(-1f), + HUGE(2f), + TINY(-2f); - public static final RelativeFont PLAIN = new RelativeFont(Font.PLAIN, 0); - public static final RelativeFont BOLD = new RelativeFont(Font.BOLD, 0); - public static final RelativeFont LARGE = new RelativeFont(1f); - public static final RelativeFont SMALL = new RelativeFont(-1f); - public static final RelativeFont HUGE = new RelativeFont(2f); - public static final RelativeFont TINY = new RelativeFont(-2f); + private static final String PROPERTY = "font"; private final int myStyle; private final float mySize; - public RelativeFont(float size) { + RelativeFont(float size) { this.myStyle = -1; this.mySize = size; } - public RelativeFont(int style, float size) { + RelativeFont(int style, float size) { this.myStyle = style & (Font.BOLD | Font.ITALIC); this.mySize = size; - if (style != myStyle) { - throw new IllegalArgumentException("style"); - } } public T install(T component) { diff --git a/platform/platform-api/src/com/intellij/ui/components/panels/HorizontalLayout.java b/platform/platform-api/src/com/intellij/ui/components/panels/HorizontalLayout.java index 9652562b6d2a..83ef4d9b0802 100644 --- a/platform/platform-api/src/com/intellij/ui/components/panels/HorizontalLayout.java +++ b/platform/platform-api/src/com/intellij/ui/components/panels/HorizontalLayout.java @@ -112,12 +112,12 @@ public final class HorizontalLayout implements LayoutManager2 { @Override public void addLayoutComponent(String name, Component component) { synchronized (component.getTreeLock()) { - if (name == null || CENTER.equalsIgnoreCase(name)) { - myCenter.add(component); - } - else if (LEFT.equalsIgnoreCase(name)) { + if (name == null || LEFT.equalsIgnoreCase(name)) { myLeft.add(component); } + else if (CENTER.equalsIgnoreCase(name)) { + myCenter.add(component); + } else if (RIGHT.equalsIgnoreCase(name)) { myRight.add(component); } @@ -242,7 +242,7 @@ public final class HorizontalLayout implements LayoutManager2 { if (result == null) { result = new Dimension(); } - else if (aligned) { + else if (aligned && center != null) { int leftWidth = left == null ? 0 : left.width; int rightWidth = right == null ? 0 : right.width; result.width += Math.abs(leftWidth - rightWidth); diff --git a/platform/platform-api/src/com/intellij/ui/components/panels/VerticalLayout.java b/platform/platform-api/src/com/intellij/ui/components/panels/VerticalLayout.java index 7b9fdd0242ba..6d27dc33d132 100644 --- a/platform/platform-api/src/com/intellij/ui/components/panels/VerticalLayout.java +++ b/platform/platform-api/src/com/intellij/ui/components/panels/VerticalLayout.java @@ -112,12 +112,12 @@ public final class VerticalLayout implements LayoutManager2 { @Override public void addLayoutComponent(String name, Component component) { synchronized (component.getTreeLock()) { - if (name == null || CENTER.equalsIgnoreCase(name)) { - myCenter.add(component); - } - else if (TOP.equalsIgnoreCase(name)) { + if (name == null || TOP.equalsIgnoreCase(name)) { myTop.add(component); } + else if (CENTER.equalsIgnoreCase(name)) { + myCenter.add(component); + } else if (BOTTOM.equalsIgnoreCase(name)) { myBottom.add(component); } @@ -242,7 +242,7 @@ public final class VerticalLayout implements LayoutManager2 { if (result == null) { result = new Dimension(); } - else if (aligned) { + else if (aligned && center != null) { int topHeight = top == null ? 0 : top.height; int bottomHeight = bottom == null ? 0 : bottom.height; result.width += Math.abs(topHeight - bottomHeight); diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/DiffManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/DiffManagerImpl.java index 9be71af4b6b8..45bb68466412 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/DiffManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/DiffManagerImpl.java @@ -42,11 +42,12 @@ import java.util.Arrays; public class DiffManagerImpl extends DiffManager implements JDOMExternalizable { public static final int FULL_DIFF_DIVIDER_POLYGONS_OFFSET = 3; - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.diff.impl.external.DiffManagerImpl"); + private static final Logger LOG = Logger.getInstance(DiffManagerImpl.class); private static final Externalizer TOOL_PATH_UPDATE = new Externalizer() { @NonNls private static final String NEW_VALUE = "newValue"; + @Override public String readValue(Element dataElement) { String path = dataElement.getAttributeValue(NEW_VALUE); if (path != null) return path; @@ -54,6 +55,7 @@ public class DiffManagerImpl extends DiffManager implements JDOMExternalizable { return prevValue != null ? prevValue.trim() : null; } + @Override public void writeValue(Element dataElement, String path) { dataElement.setAttribute(VALUE_ATTRIBUTE, path); dataElement.setAttribute(NEW_VALUE, path); @@ -75,6 +77,7 @@ public class DiffManagerImpl extends DiffManager implements JDOMExternalizable { public static final Key EDITOR_IS_DIFF_KEY = new Key("EDITOR_IS_DIFF_KEY"); private static final MarkupEditorFilter DIFF_EDITOR_FILTER = new MarkupEditorFilter() { + @Override public boolean avaliableIn(Editor editor) { return DiffUtil.isDiffEditor(editor); } @@ -96,8 +99,10 @@ public class DiffManagerImpl extends DiffManager implements JDOMExternalizable { myProperties.registerProperty(MERGE_TOOL_PARAMETERS); } + @Override public DiffTool getIdeaDiffTool() { return INTERNAL_DIFF; } + @Override public DiffTool getDiffTool() { DiffTool[] standardTools; // there is inner check in multiple tool for external viewers as well @@ -131,21 +136,25 @@ public class DiffManagerImpl extends DiffManager implements JDOMExternalizable { return new CompositeDiffTool(allTools); } + @Override public boolean registerDiffTool(@NotNull DiffTool tool) throws NullPointerException { if (myAdditionTools.contains(tool)) return false; myAdditionTools.add(tool); return true; } + @Override public void unregisterDiffTool(DiffTool tool) { myAdditionTools.remove(tool); LOG.assertTrue(!myAdditionTools.contains(tool)); } + @Override public MarkupEditorFilter getDiffEditorFilter() { return DIFF_EDITOR_FILTER; } + @Override public DiffPanel createDiffPanel(Window window, Project project, DiffTool parentTool) { return new DiffPanelImpl(window, project, true, true, FULL_DIFF_DIVIDER_POLYGONS_OFFSET, parentTool); } @@ -161,6 +170,7 @@ public class DiffManagerImpl extends DiffManager implements JDOMExternalizable { return (DiffManagerImpl)DiffManager.getInstance(); } + @Override public void readExternal(@NotNull Element element) throws InvalidDataException { myProperties.readExternal(element); readPolicy(element); @@ -191,6 +201,7 @@ public class DiffManagerImpl extends DiffManager implements JDOMExternalizable { } } + @Override public void writeExternal(@NotNull Element element) throws WriteExternalException { myProperties.writeExternal(element); if (myComparisonPolicy != null) { diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/Banner.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/Banner.java index 465f16ddd42a..19d5820c6a37 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/Banner.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/Banner.java @@ -66,9 +66,9 @@ final class Banner extends JPanel { } else { if (i > 0) { - myLeftPanel.add(HorizontalLayout.LEFT, RelativeFont.HUGE.install(new JLabel("\u203A"))); + myLeftPanel.add(RelativeFont.HUGE.install(new JLabel("\u203A"))); } - myLeftPanel.add(HorizontalLayout.LEFT, RelativeFont.BOLD.install(new JLabel(name))); + myLeftPanel.add(RelativeFont.BOLD.install(new JLabel(name))); } i += 2; } diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java index 6ac03daef35d..1522597c9cbc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java @@ -69,7 +69,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd private static final String NODE_ICON = "settings.tree.view.icon"; private static final Color WRONG_CONTENT = JBColor.RED; private static final Color MODIFIED_CONTENT = JBColor.BLUE; - public static final Color FOREGROUND = new JBColor(0x1A1A1A, 0xBBBBBB); + public static final Color FOREGROUND = new JBColor(Gray.x1A, Gray.xBB); public static final Color BACKGROUND = new JBColor(0xE6EBF0, 0x3E434C); final SimpleTree myTree; @@ -97,7 +97,6 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd TreeUtil.installActions(myTree); myTree.setOpaque(true); - myTree.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0)); myTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); @@ -116,6 +115,8 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd mySeparator = new JLabel(); mySeparator.setForeground(FOREGROUND); + mySeparator.setIconTextGap(10); + mySeparator.setBorder(BorderFactory.createEmptyBorder(1, 19, 0, 0)); myTree.addComponentListener(new ComponentAdapter() { @Override @@ -273,10 +274,10 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd return; // separator is not needed without scrolling } mySeparator.setFont(myTree.getFont()); + mySeparator.setIcon(myTree.getEmptyHandle()); int height = mySeparator.getPreferredSize().height; - String group = findGroupNameAt(0, height); + String group = findGroupNameAt(0, height + 3); if (group != null && group.equals(findGroupNameAt(0, 0))) { - mySeparator.setBorder(BorderFactory.createEmptyBorder(1, 22, 0, 0)); mySeparator.setText(group); Rectangle bounds = myScroller.getViewport().getBounds(); @@ -459,16 +460,16 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd private final class MyRenderer extends JPanel implements TreeCellRenderer { private final JLabel myTextLabel = new ErrorLabel(); - private final JLabel myNodeIcon = new JLabel(" ", SwingConstants.RIGHT); - private final JLabel myProjectIcon = new JLabel(" ", SwingConstants.LEFT); + private final JLabel myNodeIcon = new JLabel(); + private final JLabel myProjectIcon = new JLabel(); public MyRenderer() { - super(new BorderLayout()); + super(new BorderLayout(10, 0)); myNodeIcon.setName(NODE_ICON); add(BorderLayout.CENTER, myTextLabel); add(BorderLayout.WEST, myNodeIcon); add(BorderLayout.EAST, myProjectIcon); - setBorder(BorderFactory.createEmptyBorder(1, 0, 3, 0)); + setBorder(BorderFactory.createEmptyBorder(1, 10, 3, 10)); } public Component getTreeCellRendererComponent(JTree tree, @@ -554,9 +555,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd myNodeIcon.setIcon(nodeIcon); // calculate minimum size if (node != null && tree.isVisible()) { - int width = getPreferredSize().width; - width += node.myLevel * UIUtil.getTreeLeftChildIndent(); - width += node.myLevel * UIUtil.getTreeRightChildIndent(); + int width = 10 * node.myLevel + getPreferredSize().width; Insets insets = tree.getInsets(); if (insets != null) { width += insets.left + insets.right; @@ -722,19 +721,21 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd boolean hasBeenExpanded, boolean isLeaf) { if (tree != null) { - int width = tree.getWidth(); + bounds.width = tree.getWidth(); Container parent = tree.getParent(); if (parent instanceof JViewport) { JViewport viewport = (JViewport)parent; - width = viewport.getWidth() - viewport.getViewPosition().x; - } - width -= bounds.x; - if (bounds.width < width) { - bounds.width = width; + bounds.width = viewport.getWidth() - viewport.getViewPosition().x; } + bounds.width -= bounds.x; } super.paintRow(g, clipBounds, insets, bounds, path, row, isExpanded, hasBeenExpanded, isLeaf); } + + @Override + protected int getRowX(int row, int depth) { + return 10 * depth; + } } private final class MyBuilder extends FilteringTreeBuilder { diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java index c13a67fab5aa..68a5956a4866 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java @@ -43,6 +43,7 @@ import com.intellij.openapi.wm.impl.IdeGlassPaneImpl; import com.intellij.ui.*; import com.intellij.ui.components.labels.ActionLink; import com.intellij.ui.components.panels.NonOpaquePanel; +import com.intellij.util.PlatformUtils; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -171,7 +172,7 @@ public class FlatWelcomeFrame extends JFrame implements WelcomeFrameProvider, Id @Override public IdeFrame createFrame() { - return Registry.is("ide.new.welcome.screen") ? this : null; + return Registry.is("ide.new.welcome.screen") && (PlatformUtils.isIntelliJ() || PlatformUtils.isCidr()) ? this : null; } private class FlatWelcomeScreen extends JPanel implements WelcomeScreen { @@ -328,7 +329,7 @@ public class FlatWelcomeFrame extends JFrame implements WelcomeFrameProvider, Id private JComponent createRecentProjects() { JPanel panel = new JPanel(new BorderLayout()); - panel.add(new NewRecentProjectPanel(this), BorderLayout.NORTH); + panel.add(new NewRecentProjectPanel(this), BorderLayout.CENTER); panel.setBackground(getProjectsBackGround()); return panel; } diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index 49b40f376cee..ecc6739bae32 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -1379,9 +1379,9 @@ action.MarkAsOriginalTypeAction.text=Mark as action.Console.Execute.text=Execute Current Statement action.Console.Execute.description=Execute current statement in console -action.Console.History.Previous.text=Previous +action.Console.History.Previous.text=Previous history entry action.Console.History.Previous.description=Previous console history entry -action.Console.History.Next.text=Next +action.Console.History.Next.text=Next history entry action.Console.History.Next.description=Next console history entry action.Console.History.Browse.text=Browse History action.Console.History.Browse.description=Browse console history diff --git a/platform/platform-resources-en/src/messages/OptionsBundle.properties b/platform/platform-resources-en/src/messages/OptionsBundle.properties index 3787905fa375..ef73f9f67c3c 100644 --- a/platform/platform-resources-en/src/messages/OptionsBundle.properties +++ b/platform/platform-resources-en/src/messages/OptionsBundle.properties @@ -295,4 +295,5 @@ exportable.CodeFoldingSettings.presentable.name=Code Folding exportable.XmlFoldingSettings.presentable.name=XML Code Folding exportable.XmlSettings.presentable.name=XML exportable.PlaybackDebugger.presentable.name=Playback Debugger -exportable.XmlEditorOptions.presentable.name=XML Editor \ No newline at end of file +exportable.XmlEditorOptions.presentable.name=XML Editor +exportable.BuiltInServerOptions.presentable.name=Built-in server \ No newline at end of file diff --git a/platform/platform-resources/src/META-INF/XmlPlugin.xml b/platform/platform-resources/src/META-INF/XmlPlugin.xml index 3d507d2dd435..49295bb47cda 100644 --- a/platform/platform-resources/src/META-INF/XmlPlugin.xml +++ b/platform/platform-resources/src/META-INF/XmlPlugin.xml @@ -537,9 +537,8 @@ - - - + + diff --git a/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java b/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java index 024ba53529ec..fb2fcd2a31ec 100644 --- a/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java @@ -631,7 +631,12 @@ public abstract class PlatformTestCase extends UsefulTestCase implements DataPro } catch (Throwable e) { CompositeException result = new CompositeException(e); - disposeProject(result); + try { + tearDown(); + } + catch (Throwable th) { + result.add(th); + } throw result; } try { diff --git a/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java b/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java index fcf0e225ea5f..298acca81199 100644 --- a/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java +++ b/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java @@ -121,12 +121,23 @@ public class PlatformTestUtil { } public static String print(JTree tree, boolean withSelection) { - return print(tree, withSelection, null); + return print(tree, tree.getModel().getRoot(), withSelection, null, null); + } + + public static String print(JTree tree, Object root, @Nullable Queryable.PrintInfo printInfo, boolean withSelection) { + return print(tree, root, withSelection, printInfo, null); } public static String print(JTree tree, boolean withSelection, @Nullable Condition nodePrintCondition) { + return print(tree, tree.getModel().getRoot(), withSelection, null, nodePrintCondition); + } + + public static String print(JTree tree, Object root, + boolean withSelection, + @Nullable Queryable.PrintInfo printInfo, + @Nullable Condition nodePrintCondition) { StringBuilder buffer = new StringBuilder(); - final Collection strings = printAsList(tree, withSelection, nodePrintCondition); + final Collection strings = printAsList(tree, root, withSelection, printInfo, nodePrintCondition); for (String string : strings) { buffer.append(string).append("\n"); } @@ -134,9 +145,15 @@ public class PlatformTestUtil { } public static Collection printAsList(JTree tree, boolean withSelection, @Nullable Condition nodePrintCondition) { + return printAsList(tree, tree.getModel().getRoot(), withSelection, null, nodePrintCondition); + } + + private static Collection printAsList(JTree tree, Object root, + boolean withSelection, + @Nullable Queryable.PrintInfo printInfo, + Condition nodePrintCondition) { Collection strings = new ArrayList(); - Object root = tree.getModel().getRoot(); - printImpl(tree, root, strings, 0, withSelection, nodePrintCondition); + printImpl(tree, root, strings, 0, withSelection, printInfo, nodePrintCondition); return strings; } @@ -145,13 +162,14 @@ public class PlatformTestUtil { Collection strings, int level, boolean withSelection, + @Nullable Queryable.PrintInfo printInfo, @Nullable Condition nodePrintCondition) { DefaultMutableTreeNode defaultMutableTreeNode = (DefaultMutableTreeNode)root; final Object userObject = defaultMutableTreeNode.getUserObject(); String nodeText; if (userObject != null) { - nodeText = toString(userObject, null); + nodeText = toString(userObject, printInfo); } else { nodeText = "null"; @@ -183,7 +201,7 @@ public class PlatformTestUtil { int childCount = tree.getModel().getChildCount(root); if (expanded) { for (int i = 0; i < childCount; i++) { - printImpl(tree, tree.getModel().getChild(root, i), strings, level + 1, withSelection, nodePrintCondition); + printImpl(tree, tree.getModel().getChild(root, i), strings, level + 1, withSelection, printInfo, nodePrintCondition); } } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/issueLinks/IssueLinkRenderer.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/issueLinks/IssueLinkRenderer.java index e40102dd3d59..72408def3e33 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/issueLinks/IssueLinkRenderer.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/issueLinks/IssueLinkRenderer.java @@ -18,6 +18,7 @@ package com.intellij.openapi.vcs.changes.issueLinks; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vcs.IssueNavigationConfiguration; +import com.intellij.ui.JBColor; import com.intellij.ui.SimpleColoredComponent; import com.intellij.ui.SimpleTextAttributes; import com.intellij.util.Consumer; @@ -84,7 +85,6 @@ public class IssueLinkRenderer { } private static SimpleTextAttributes getLinkAttributes(final SimpleTextAttributes baseStyle) { - return (baseStyle.getStyle() & SimpleTextAttributes.STYLE_BOLD) != 0 ? - SimpleTextAttributes.LINK_BOLD_ATTRIBUTES : SimpleTextAttributes.LINK_ATTRIBUTES; + return new SimpleTextAttributes(baseStyle.getStyle() | SimpleTextAttributes.STYLE_UNDERLINE, JBColor.blue); } } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/cloneable/CloneInNonCloneableClassInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/cloneable/CloneInNonCloneableClassInspection.java index 0d210bff4dd6..23f5f096ffe4 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/cloneable/CloneInNonCloneableClassInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/cloneable/CloneInNonCloneableClassInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2013 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2014 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. @@ -17,6 +17,7 @@ package com.siyeh.ig.cloneable; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiModifier; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; @@ -67,6 +68,9 @@ public class CloneInNonCloneableClassInspection extends BaseInspection { if (CloneUtils.isCloneable(containingClass)) { return; } + if (method.hasModifierProperty(PsiModifier.FINAL) && CloneUtils.onlyThrowsCloneNotSupportedException(method)) { + return; + } registerMethodError(method, containingClass); } } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/CloneUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/CloneUtils.java index e54b5786bada..4513a60bd033 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/CloneUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/CloneUtils.java @@ -74,9 +74,8 @@ public class CloneUtils { if (!(statement instanceof PsiThrowStatement)) { return false; } - final PsiThrowStatement throwStatement = - (PsiThrowStatement)statement; - final PsiExpression exception = throwStatement.getException(); + final PsiThrowStatement throwStatement = (PsiThrowStatement)statement; + final PsiExpression exception = ParenthesesUtils.stripParentheses(throwStatement.getException()); if (!(exception instanceof PsiNewExpression)) { return false; } diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/style/ProblematicWhitespaceInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/style/ProblematicWhitespaceInspection.java index f2b30d04c370..a307a1ca2c4e 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/style/ProblematicWhitespaceInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/style/ProblematicWhitespaceInspection.java @@ -21,8 +21,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorSettings; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.fileTypes.FileTypes; -import com.intellij.openapi.fileTypes.StdFileTypes; +import com.intellij.openapi.fileTypes.LanguageFileType; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiDocumentManager; @@ -105,24 +104,11 @@ public class ProblematicWhitespaceInspection extends BaseInspection { private static class ProblematicWhitespaceVisitor extends BaseInspectionVisitor { - private static boolean isLanguageFileType(FileType fileType) { - return (fileType != StdFileTypes.GUI_DESIGNER_FORM) && - (fileType != StdFileTypes.IDEA_MODULE) && - (fileType != StdFileTypes.IDEA_PROJECT) && - (fileType != StdFileTypes.IDEA_WORKSPACE) && - (fileType != FileTypes.ARCHIVE) && - (fileType != FileTypes.UNKNOWN) && - (fileType != FileTypes.PLAIN_TEXT) && - //!(fileType instanceof AbstractFileType) && // not sure about this one - !fileType.isBinary() && - !fileType.isReadOnly(); - } - @Override public void visitFile(PsiFile file) { super.visitFile(file); final FileType fileType = file.getFileType(); - if (!isLanguageFileType(fileType)) { + if (!(fileType instanceof LanguageFileType)) { return; } final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(file.getProject()); diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/cloneable/CloneCallsConstructorInspection.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/cloneable/CloneCallsConstructorInspection.java deleted file mode 100644 index 46814725bfb6..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/cloneable/CloneCallsConstructorInspection.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.siyeh.igtest.cloneable; - -public class CloneCallsConstructorInspection implements Cloneable -{ - - public void foo() - { - - } - - public Object clone() - { - return new CloneCallsConstructorInspection(); - } -} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/cloneable/clone_in_non_cloneable_class/CloneInNonCloneableClass.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/cloneable/clone_in_non_cloneable_class/CloneInNonCloneableClass.java new file mode 100644 index 000000000000..d1f84f16448f --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/cloneable/clone_in_non_cloneable_class/CloneInNonCloneableClass.java @@ -0,0 +1,13 @@ +public class CloneInNonCloneableClass { + + public final Object clone() throws CloneNotSupportedException { + // don't warn on final method that only throws CloneNotSupportedException + throw new CloneNotSupportedException(); + } +} +class AB { + @Override + protected Object clone() throws CloneNotSupportedException { + return super.clone(); + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/memory/return_of_inner_class/ReturnOfInnerClass.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/memory/return_of_inner_class/ReturnOfInnerClass.java index bcf8358a776a..ec22e0a3b2dd 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/memory/return_of_inner_class/ReturnOfInnerClass.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/memory/return_of_inner_class/ReturnOfInnerClass.java @@ -17,4 +17,12 @@ public class ReturnOfInnerClass { private Object four() { return new B(); } + + protected Object five() { + return new B(); + } + + Object six() { + return new B(); + } } \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/execution/runners/ConsoleExecuteActionHandler.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/cloneable/CloneInNonCloneableClassInspectionTest.java similarity index 51% rename from platform/lang-impl/src/com/intellij/execution/runners/ConsoleExecuteActionHandler.java rename to plugins/InspectionGadgets/testsrc/com/siyeh/ig/cloneable/CloneInNonCloneableClassInspectionTest.java index 52e8ebf70bb5..75ee9ad07dbe 100644 --- a/platform/lang-impl/src/com/intellij/execution/runners/ConsoleExecuteActionHandler.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/cloneable/CloneInNonCloneableClassInspectionTest.java @@ -13,19 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.execution.runners; +package com.siyeh.ig.cloneable; -import com.intellij.execution.console.ProcessBackedConsoleExecuteActionHandler; -import com.intellij.execution.process.ProcessHandler; +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; -@SuppressWarnings({"ClassNameSameAsAncestorName", "UnusedDeclaration"}) -@Deprecated /** - * @deprecated Use {@link ProcessBackedConsoleExecuteActionHandler} - * to remove in IDEA 15 + * @author Bas Leijdekkers */ -public class ConsoleExecuteActionHandler extends ProcessBackedConsoleExecuteActionHandler { - public ConsoleExecuteActionHandler(ProcessHandler processHandler, boolean preserveMarkup) { - super(processHandler, preserveMarkup); +public class CloneInNonCloneableClassInspectionTest extends LightInspectionTestCase { + @Override + protected InspectionProfileEntry getInspection() { + return new CloneInNonCloneableClassInspection(); } -} \ No newline at end of file + + public void testCloneInNonCloneableClass() { doTest(); } +} diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/memory/ReturnOfInnerClassInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/memory/ReturnOfInnerClassInspectionTest.java index 3c01ea57ac5f..1aa17027caa5 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/memory/ReturnOfInnerClassInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/memory/ReturnOfInnerClassInspectionTest.java @@ -17,7 +17,6 @@ package com.siyeh.ig.memory; import com.intellij.codeInspection.InspectionProfileEntry; import com.siyeh.ig.LightInspectionTestCase; -import junit.framework.TestCase; import org.jetbrains.annotations.Nullable; /** @@ -30,6 +29,8 @@ public class ReturnOfInnerClassInspectionTest extends LightInspectionTestCase { @Nullable @Override protected InspectionProfileEntry getInspection() { - return new ReturnOfInnerClassInspection(); + final ReturnOfInnerClassInspection inspection = new ReturnOfInnerClassInspection(); + inspection.ignoreNonPublic = true; + return inspection; } } \ No newline at end of file diff --git a/plugins/IntentionPowerPak/src/intentionDescriptions/ReplaceDiamondWithExplicitTypeArgumentsIntention/description.html b/plugins/IntentionPowerPak/src/intentionDescriptions/ReplaceDiamondWithExplicitTypeArgumentsIntention/description.html index 0e8f45004167..42bf62b912de 100644 --- a/plugins/IntentionPowerPak/src/intentionDescriptions/ReplaceDiamondWithExplicitTypeArgumentsIntention/description.html +++ b/plugins/IntentionPowerPak/src/intentionDescriptions/ReplaceDiamondWithExplicitTypeArgumentsIntention/description.html @@ -1,6 +1,6 @@ -This intention replaces a <> (diamond) with the equivalent +This intention replaces a <> (diamond) with the equivalent explicit type arguments.

diff --git a/plugins/git4idea/src/git4idea/actions/GitMergeAction.java b/plugins/git4idea/src/git4idea/actions/GitMergeAction.java index fbe465caa289..c2e172525307 100644 --- a/plugins/git4idea/src/git4idea/actions/GitMergeAction.java +++ b/plugins/git4idea/src/git4idea/actions/GitMergeAction.java @@ -25,6 +25,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.update.ActionInfo; +import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import git4idea.GitRevisionNumber; import git4idea.GitUtil; @@ -121,7 +122,7 @@ abstract class GitMergeAction extends GitRepositoryAction { GitRepositoryManager repositoryManager = GitUtil.getRepositoryManager(project); VirtualFile root = repository.getRoot(); if (result.success()) { - root.refresh(false, true); + VfsUtil.markDirtyAndRefresh(false, true, false, root); List exceptions = new ArrayList(); GitMergeUtil.showUpdates(this, project, exceptions, root, currentRev, beforeLabel, getActionName(), ActionInfo.UPDATE); repositoryManager.updateRepository(root); diff --git a/plugins/git4idea/src/git4idea/push/GitPushSource.java b/plugins/git4idea/src/git4idea/push/GitPushSource.java index 146c854564a8..f87037c58c4b 100644 --- a/plugins/git4idea/src/git4idea/push/GitPushSource.java +++ b/plugins/git4idea/src/git4idea/push/GitPushSource.java @@ -15,26 +15,63 @@ */ package git4idea.push; +import com.intellij.dvcs.DvcsUtil; import com.intellij.dvcs.push.PushSource; import git4idea.GitLocalBranch; import org.jetbrains.annotations.NotNull; -class GitPushSource implements PushSource { +abstract class GitPushSource implements PushSource { - @NotNull private final GitLocalBranch myBranch; - - GitPushSource(@NotNull GitLocalBranch branch) { - myBranch = branch; + @NotNull + static GitPushSource create(@NotNull GitLocalBranch branch) { + return new OnBranch(branch); } @NotNull - @Override - public String getPresentation() { - return myBranch.getName(); + static GitPushSource create(@NotNull String revision) { + return new DetachedHead(revision); } @NotNull - public GitLocalBranch getBranch() { - return myBranch; + abstract GitLocalBranch getBranch(); + + private static class OnBranch extends GitPushSource { + @NotNull private final GitLocalBranch myBranch; + + private OnBranch(@NotNull GitLocalBranch branch) { + myBranch = branch; + } + + @NotNull + @Override + public String getPresentation() { + return myBranch.getName(); + } + + @NotNull + @Override + GitLocalBranch getBranch() { + return myBranch; + } + } + + private static class DetachedHead extends GitPushSource { + @NotNull private final String myRevision; + + public DetachedHead(@NotNull String revision) { + myRevision = revision; + } + + @NotNull + @Override + public String getPresentation() { + return DvcsUtil.getShortHash(myRevision); + } + + @NotNull + @Override + GitLocalBranch getBranch() { + throw new IllegalStateException("Push is not allowed from detached HEAD"); + } } } diff --git a/plugins/git4idea/src/git4idea/push/GitPushSupport.java b/plugins/git4idea/src/git4idea/push/GitPushSupport.java index 77ec5ca6fdc8..b8c55280c47b 100644 --- a/plugins/git4idea/src/git4idea/push/GitPushSupport.java +++ b/plugins/git4idea/src/git4idea/push/GitPushSupport.java @@ -81,6 +81,9 @@ public class GitPushSupport extends PushSupport not possible to push + GitLocalBranch currentBranch = repository.getCurrentBranch(); + return currentBranch != null + ? GitPushSource.create(currentBranch) + : GitPushSource.create(ObjectUtils.assertNotNull(repository.getCurrentRevision())); // fresh repository is on branch } @NotNull diff --git a/plugins/git4idea/src/git4idea/push/GitPushTargetPanel.java b/plugins/git4idea/src/git4idea/push/GitPushTargetPanel.java index 4a54fc6534c9..40eaf69b9f7b 100644 --- a/plugins/git4idea/src/git4idea/push/GitPushTargetPanel.java +++ b/plugins/git4idea/src/git4idea/push/GitPushTargetPanel.java @@ -24,7 +24,6 @@ import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.ListPopup; import com.intellij.openapi.ui.popup.PopupStep; import com.intellij.openapi.ui.popup.util.BaseListPopupStep; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.ColoredTreeCellRenderer; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.awt.RelativePoint; @@ -51,30 +50,40 @@ class GitPushTargetPanel extends PushTargetPanel { private static final Comparator REMOTE_BRANCH_COMPARATOR = new MyRemoteBranchComparator(); private static final String SEPARATOR = " : "; - private static final String NO_REMOTES = "No remotes"; - private final GitRepository myRepository; - private final PushTargetTextField myTargetTextField; - private final JLabel myRemoteLabel; + @NotNull private final GitRepository myRepository; + @NotNull private final PushTargetTextField myTargetTextField; + @NotNull private final JLabel myRemoteLabel; + @NotNull private final ExtraEditControl myEditRemoteControl; @Nullable private GitPushTarget myCurrentTarget; + @Nullable private String myError; @Nullable private Runnable myFireOnChangeAction; - @NotNull private ExtraEditControl myEditRemoteControl; public GitPushTargetPanel(@NotNull GitRepository repository, @Nullable GitPushTarget defaultTarget) { myRepository = repository; myCurrentTarget = defaultTarget; - String initialBranch; - String initialRemote; + String initialBranch = ""; + String initialRemote = ""; if (defaultTarget == null) { - initialBranch = ""; - initialRemote = NO_REMOTES; + if (repository.getCurrentBranch() == null) { + myError = "Detached HEAD"; + } + else if (repository.getRemotes().isEmpty()) { + myError = "No remotes"; + } + else if (repository.isFresh()) { + myError = "Empty repository"; + } + else { + myError = "Can't push"; + } } else { initialBranch = getTextFieldText(defaultTarget); - initialRemote = getRemoteLabelText(defaultTarget.getBranch().getRemote().getName()); + initialRemote = defaultTarget.getBranch().getRemote().getName(); } myEditRemoteControl = new ExtraEditControl() { @@ -89,7 +98,12 @@ class GitPushTargetPanel extends PushTargetPanel { setLayout(new BorderLayout()); setOpaque(false); - add(myRemoteLabel, BorderLayout.WEST); + JPanel remoteAndSeparator = new JPanel(new BorderLayout()); + remoteAndSeparator.setOpaque(false); + remoteAndSeparator.add(myRemoteLabel, BorderLayout.CENTER); + remoteAndSeparator.add(new JBLabel(SEPARATOR), BorderLayout.EAST); + + add(remoteAndSeparator, BorderLayout.WEST); add(myTargetTextField, BorderLayout.CENTER); updateTextField(); } @@ -107,7 +121,7 @@ class GitPushTargetPanel extends PushTargetPanel { ListPopup popup = JBPopupFactory.getInstance().createListPopup(new BaseListPopupStep(null, remotes) { @Override public PopupStep onChosen(String selectedValue, boolean finalChoice) { - myRemoteLabel.setText(getRemoteLabelText(selectedValue)); + myRemoteLabel.setText(selectedValue); if (myFireOnChangeAction != null) { myFireOnChangeAction.run(); } @@ -129,9 +143,8 @@ class GitPushTargetPanel extends PushTargetPanel { @Override public void render(@NotNull final ColoredTreeCellRenderer renderer) { - String targetName = myTargetTextField.getText(); - if (StringUtil.isEmptyOrSpaces(targetName)) { - renderer.append(NO_REMOTES, SimpleTextAttributes.ERROR_ATTRIBUTES, this); + if (myError != null) { + renderer.append(myError, SimpleTextAttributes.ERROR_ATTRIBUTES); } else { String currentRemote = myRemoteLabel.getText(); @@ -141,6 +154,7 @@ class GitPushTargetPanel extends PushTargetPanel { else { renderer.append(currentRemote, SimpleTextAttributes.REGULAR_ATTRIBUTES); } + renderer.append(SEPARATOR, SimpleTextAttributes.REGULAR_ATTRIBUTES); GitPushTarget target = getValue(); if (target.isNewBranchCreated()) { @@ -161,10 +175,6 @@ class GitPushTargetPanel extends PushTargetPanel { return (target != null ? target.getBranch().getNameForRemoteOperations() : ""); } - private static String getRemoteLabelText(@NotNull String selectedValue) { - return selectedValue + SEPARATOR; - } - @Override public void fireOnCancel() { myTargetTextField.setText(getTextFieldText(myCurrentTarget)); @@ -172,7 +182,10 @@ class GitPushTargetPanel extends PushTargetPanel { @Override public void fireOnChange() { - String remoteName = getEnteredRemote(); + if (myError == null) { + return; + } + String remoteName = myRemoteLabel.getText(); String branchName = myTargetTextField.getText(); try { myCurrentTarget = GitPushTarget.parse(myRepository, remoteName, branchName); @@ -185,9 +198,11 @@ class GitPushTargetPanel extends PushTargetPanel { @Nullable @Override public ValidationInfo verify() { + if (myError != null) { + return new ValidationInfo(myError, myTargetTextField); + } try { - String remoteLabel = getEnteredRemote(); - GitPushTarget.parse(myRepository, remoteLabel, myTargetTextField.getText()); + GitPushTarget.parse(myRepository, myRemoteLabel.getText(), myTargetTextField.getText()); return null; } catch (ParseException e) { @@ -195,17 +210,12 @@ class GitPushTargetPanel extends PushTargetPanel { } } + @SuppressWarnings("NullableProblems") @Override public void setFireOnChangeAction(@NotNull Runnable action) { myFireOnChangeAction = action; } - @Nullable - private String getEnteredRemote() { - String text = myRemoteLabel.getText(); - return text.equals(NO_REMOTES) ? null : text.replace(SEPARATOR, ""); - } - @NotNull public static List getTargetNames(@NotNull GitRepository repository) { List remoteBranches = ContainerUtil.sorted(repository.getBranches().getRemoteBranches(), REMOTE_BRANCH_COMPARATOR); @@ -219,7 +229,7 @@ class GitPushTargetPanel extends PushTargetPanel { private static class MyRemoteBranchComparator implements Comparator { @Override - public int compare(GitRemoteBranch o1, GitRemoteBranch o2) { + public int compare(@NotNull GitRemoteBranch o1, @NotNull GitRemoteBranch o2) { String remoteName1 = o1.getRemote().getName(); String remoteName2 = o2.getRemote().getName(); int remoteComparison = remoteName1.compareTo(remoteName2); diff --git a/plugins/git4idea/tests/git4idea/push/GitPushOperationBaseTest.java b/plugins/git4idea/tests/git4idea/push/GitPushOperationBaseTest.java index 3ec0475615b9..819ad56e6938 100644 --- a/plugins/git4idea/tests/git4idea/push/GitPushOperationBaseTest.java +++ b/plugins/git4idea/tests/git4idea/push/GitPushOperationBaseTest.java @@ -138,7 +138,7 @@ abstract class GitPushOperationBaseTest extends GitPlatformTest { else { newBranch = false; } - return new PushSpec(new GitPushSource(source), new GitPushTarget(target, newBranch)); + return new PushSpec(GitPushSource.create(source), new GitPushTarget(target, newBranch)); } @NotNull diff --git a/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSettingsPanel.form b/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSettingsPanel.form index a3549a4bc1e2..a31ae1af9432 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSettingsPanel.form +++ b/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSettingsPanel.form @@ -1,6 +1,6 @@
- + @@ -13,7 +13,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -167,13 +167,13 @@ - + - + @@ -204,6 +204,15 @@ + + + + + + + + + diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/ClassWriter.java index 0bdd8a85b69d..ad6a95254aa2 100644 --- a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -254,8 +254,11 @@ public class ClassWriter { if (hasContent) { buffer.appendLineSeparator(); + startLine++; } - classToJava(inner, buffer, indent + 1, tracer); + BytecodeMappingTracer class_tracer = new BytecodeMappingTracer(startLine); + classToJava(inner, buffer, indent + 1, class_tracer); + startLine = buffer.countLines(); hasContent = true; } @@ -840,7 +843,7 @@ public class ClassWriter { // save total lines // TODO: optimize - tracer.setCurrentSourceLine(buffer.countLines(start_index_method)); + //tracer.setCurrentSourceLine(buffer.countLines(start_index_method)); return !hideMethod; } diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java index 66cfa630d6cc..20d6942a26d7 100644 --- a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java @@ -280,6 +280,7 @@ public class ClassesProcessor { } //buffer.append(lineSeparator); + total_offset_lines = buffer.countLines(); buffer.append(classBuffer); if (DecompilerContext.getOption(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING)) { diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/collectors/BytecodeSourceMapper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/collectors/BytecodeSourceMapper.java index ce328a9a63b7..26e6f78cf0f1 100644 --- a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/collectors/BytecodeSourceMapper.java +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/collectors/BytecodeSourceMapper.java @@ -97,7 +97,7 @@ public class BytecodeSourceMapper { buffer.append("Lines mapping:").appendLineSeparator(); Map sorted = new TreeMap(linesMapping); for (Entry entry : sorted.entrySet()) { - buffer.append(entry.getKey()).append(" <-> ").append(entry.getValue()).appendLineSeparator(); + buffer.append(entry.getKey()).append(" <-> ").append(entry.getValue()+ offset_total + 1).appendLineSeparator(); } } diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java index 1cddbbc76634..f6287c2269a7 100644 --- a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java @@ -187,7 +187,6 @@ public class CatchStatement extends Statement { tracer.incrementCurrentSourceLine(); buf.append(ExprProcessor.jmpWrapper(stats.get(i), indent + 1, true, tracer)).append(indstr) .append("}"); - tracer.incrementCurrentSourceLine(); } buf.append(new_line_separator); diff --git a/plugins/java-decompiler/engine/testData/classes/pkg/TestClassSimpleBytecodeMapping$InnerClass.class b/plugins/java-decompiler/engine/testData/classes/pkg/TestClassSimpleBytecodeMapping$InnerClass.class new file mode 100644 index 000000000000..bd3481b595d2 Binary files /dev/null and b/plugins/java-decompiler/engine/testData/classes/pkg/TestClassSimpleBytecodeMapping$InnerClass.class differ diff --git a/plugins/java-decompiler/engine/testData/classes/pkg/TestClassSimpleBytecodeMapping$InnerClass2.class b/plugins/java-decompiler/engine/testData/classes/pkg/TestClassSimpleBytecodeMapping$InnerClass2.class new file mode 100644 index 000000000000..b28c357352dd Binary files /dev/null and b/plugins/java-decompiler/engine/testData/classes/pkg/TestClassSimpleBytecodeMapping$InnerClass2.class differ diff --git a/plugins/java-decompiler/engine/testData/classes/pkg/TestClassSimpleBytecodeMapping.class b/plugins/java-decompiler/engine/testData/classes/pkg/TestClassSimpleBytecodeMapping.class index 4e4f8fbe16f8..010c748a4b0e 100644 Binary files a/plugins/java-decompiler/engine/testData/classes/pkg/TestClassSimpleBytecodeMapping.class and b/plugins/java-decompiler/engine/testData/classes/pkg/TestClassSimpleBytecodeMapping.class differ diff --git a/plugins/java-decompiler/engine/testData/results/TestClassSimpleBytecodeMapping.dec b/plugins/java-decompiler/engine/testData/results/TestClassSimpleBytecodeMapping.dec index e5dfb1c8a344..5647f76f5f96 100644 --- a/plugins/java-decompiler/engine/testData/results/TestClassSimpleBytecodeMapping.dec +++ b/plugins/java-decompiler/engine/testData/results/TestClassSimpleBytecodeMapping.dec @@ -8,6 +8,7 @@ public class TestClassSimpleBytecodeMapping { System.out.println("Runnable"); } }); + this.test2("1"); if(Math.random() > 0.0D) { System.out.println("0"); return 0; @@ -17,9 +18,30 @@ public class TestClassSimpleBytecodeMapping { } } + public void test2(String var1) { + try { + Integer.parseInt(var1); + } catch (Exception var3) { + System.out.println(var3); + } + + } + void run(Runnable var1) { var1.run(); } + + public class InnerClass2 { + public void print() { + System.out.println("Inner2"); + } + } + + public class InnerClass { + public void print() { + System.out.println("Inner"); + } + } } class 'pkg/TestClassSimpleBytecodeMapping$1' { @@ -36,34 +58,60 @@ class 'pkg/TestClassSimpleBytecodeMapping' { 3 4 5 4 11 5 - 14 10 + 15 10 17 10 - 18 10 - 19 10 - 1c 11 + 1a 11 + 1d 11 + 1e 11 1f 11 - 21 11 - 24 12 + 22 12 25 12 - 26 14 - 29 14 - 2b 14 - 2e 15 + 27 12 + 2a 13 + 2b 13 + 2c 15 2f 15 + 31 15 + 34 16 + 35 16 + } + + method 'test2 (Ljava/lang/String;)V' { + 1 22 } method 'run (Ljava/lang/Runnable;)V' { - 1 20 + 1 30 + } +} + +class 'pkg/TestClassSimpleBytecodeMapping$InnerClass2' { + method 'print ()V' { + 0 35 + 3 35 + 5 35 + } +} + +class 'pkg/TestClassSimpleBytecodeMapping$InnerClass' { + method 'print ()V' { + 0 41 + 3 41 + 5 41 } } Lines mapping: -12 <-> 2 -14 <-> 3 -17 <-> 5 -21 <-> 8 -22 <-> 9 -23 <-> 10 -25 <-> 12 -26 <-> 13 -31 <-> 18 +12 <-> 5 +14 <-> 6 +17 <-> 8 +21 <-> 11 +23 <-> 12 +24 <-> 13 +25 <-> 14 +27 <-> 16 +28 <-> 17 +34 <-> 23 +42 <-> 42 +47 <-> 31 +52 <-> 36 diff --git a/plugins/java-decompiler/engine/testData/src/pkg/TestClassSimpleBytecodeMapping.java b/plugins/java-decompiler/engine/testData/src/pkg/TestClassSimpleBytecodeMapping.java index 0e9ecc2d3201..bc07e46bcaa8 100644 --- a/plugins/java-decompiler/engine/testData/src/pkg/TestClassSimpleBytecodeMapping.java +++ b/plugins/java-decompiler/engine/testData/src/pkg/TestClassSimpleBytecodeMapping.java @@ -18,6 +18,8 @@ public class TestClassSimpleBytecodeMapping { } }); + test2("1"); + if(Math.random() > 0) { System.out.println("0"); return 0; @@ -27,7 +29,27 @@ public class TestClassSimpleBytecodeMapping { } } + public void test2(String a) { + try { + Integer.parseInt(a); + } catch (Exception e) { + System.out.println(e); + } + } + + public class InnerClass { + public void print() { + System.out.println("Inner"); + } + } + void run(Runnable r) { r.run(); } + + public class InnerClass2 { + public void print() { + System.out.println("Inner2"); + } + } } diff --git a/plugins/properties/testData/propertiesFile/enter/Performance.properties b/plugins/properties/testData/propertiesFile/enter/Performance.properties index 085f72c989cb..be0a19127c00 100644 --- a/plugins/properties/testData/propertiesFile/enter/Performance.properties +++ b/plugins/properties/testData/propertiesFile/enter/Performance.properties @@ -5593,9 +5593,9 @@ action.MarkAsOriginalTypeAction.text=Mark as action.Console.Execute.text=Execute Current Statement action.Console.Execute.description=Execute current statement in console -action.Console.History.Previous.text=Previous +action.Console.History.Previous.text=Previous history entry action.Console.History.Previous.description=Previous console history entry -action.Console.History.Next.text=Next +action.Console.History.Next.text=Next history entry action.Console.History.Next.description=Next console history entry action.Console.History.Browse.text=Browse History action.Console.History.Browse.description=Browse console history diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfigurable.form b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfigurable.form index 822ca320c43b..b3d5afb2e2d4 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfigurable.form +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfigurable.form @@ -3,7 +3,7 @@ - + @@ -240,7 +240,7 @@
- + @@ -250,7 +250,7 @@ - + @@ -264,7 +264,7 @@ - + @@ -304,19 +304,20 @@ - + - + - + + @@ -328,30 +329,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -360,102 +337,159 @@ - + + + + + + + + + + + - + - + - + - + - + - + - + - - - - - - + - + - - - - - - - - - - - - - - - - - - - + - + - + + - + - + + + + + + + + + - + - + + + + + + + + + + + - + - + + + - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + + - + + + + + + + + + + diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskFileAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskFileAction.java index a379ccf18239..b1ff29b58270 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskFileAction.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskFileAction.java @@ -125,10 +125,12 @@ public class StudyRefreshTaskFileAction extends DumbAwareAction { patternText.append("\n"); } int patternLength = patternText.length(); - if (patternText.charAt(patternLength - 1) == '\n') { - patternText.delete(patternLength - 1, patternLength); + if (patternLength != 0) { + if (patternText.charAt(patternLength - 1) == '\n') { + patternText.delete(patternLength - 1, patternLength); + } + document.setText(patternText); } - document.setText(patternText); } catch (FileNotFoundException e) { LOG.error(e); diff --git a/updater/src/com/intellij/updater/BaseDeleteAction.java b/updater/src/com/intellij/updater/BaseDeleteAction.java new file mode 100644 index 000000000000..510ec97c892b --- /dev/null +++ b/updater/src/com/intellij/updater/BaseDeleteAction.java @@ -0,0 +1,59 @@ +package com.intellij.updater; + +import java.io.DataInputStream; +import java.io.File; +import java.io.IOException; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +public abstract class BaseDeleteAction extends PatchAction { + public BaseDeleteAction(String path, long checksum) { + super(path, checksum); + } + + public BaseDeleteAction(DataInputStream in) throws IOException { + super(in); + } + + @Override + public void doBuildPatchFile(File olderFile, File newerFile, ZipOutputStream patchOutput) throws IOException { + // do nothing + } + + @Override + protected ValidationResult doValidate(File toFile) throws IOException { + ValidationResult result = doValidateAccess(toFile, ValidationResult.Action.DELETE); + if (result != null) return result; + + if (toFile.exists() && isModified(toFile)) { + return new ValidationResult(ValidationResult.Kind.CONFLICT, + myPath, + ValidationResult.Action.DELETE, + "Modified", + ValidationResult.Option.DELETE, + ValidationResult.Option.KEEP); + } + return null; + } + + @Override + protected boolean shouldApplyOn(File toFile) { + return toFile.exists(); + } + + @Override + protected void doApply(ZipFile patchFile, File toFile) throws IOException { + Utils.delete(toFile); + } + + protected void doBackup(File toFile, File backupFile) throws IOException { + Utils.copy(toFile, backupFile); + } + + protected void doRevert(File toFile, File backupFile) throws IOException { + if (!toFile.exists() || toFile.isDirectory() || isModified(toFile)) { + Utils.delete(toFile); // make sure there is no directory remained on this path (may remain from previous 'create' actions + Utils.copy(backupFile, toFile); + } + } +} diff --git a/updater/src/com/intellij/updater/BaseUpdateAction.java b/updater/src/com/intellij/updater/BaseUpdateAction.java index 67a16f8eb4ca..1d5e5a1cc78f 100644 --- a/updater/src/com/intellij/updater/BaseUpdateAction.java +++ b/updater/src/com/intellij/updater/BaseUpdateAction.java @@ -35,13 +35,7 @@ public abstract class BaseUpdateAction extends PatchAction { protected void replaceUpdated(File from, File dest) throws IOException { // on OS X code signing caches seem to be associated with specific file ids, so we need to remove the original file. - if (!dest.delete()) { - if (Utils.isWindows()) { - throw new RetryException("Cannot delete file " + dest); - } else { - throw new IOException("Cannot delete file " + dest); - } - } + if (!dest.delete()) throw new IOException("Cannot delete file " + dest); Utils.copy(from, dest); } diff --git a/updater/src/com/intellij/updater/CreateAction.java b/updater/src/com/intellij/updater/CreateAction.java index 5b0f8df595a5..dd44c35f5df1 100644 --- a/updater/src/com/intellij/updater/CreateAction.java +++ b/updater/src/com/intellij/updater/CreateAction.java @@ -42,6 +42,11 @@ public class CreateAction extends PatchAction { return null; } + @Override + protected boolean isModified(File toFile) throws IOException { + return false; + } + @Override protected void doApply(ZipFile patchFile, File toFile) throws IOException { prepareToWriteFile(toFile); @@ -59,15 +64,7 @@ public class CreateAction extends PatchAction { private static void prepareToWriteFile(File file) throws IOException { if (file.exists()) { - try { - Utils.delete(file); - } catch (IOException e) { - if (Utils.isWindows() && file.exists()) { - throw new RetryException(e); - } else { - throw e; - } - } + Utils.delete(file); return; } diff --git a/updater/src/com/intellij/updater/DeleteAction.java b/updater/src/com/intellij/updater/DeleteAction.java index e47588e150ee..e197cbded8e2 100644 --- a/updater/src/com/intellij/updater/DeleteAction.java +++ b/updater/src/com/intellij/updater/DeleteAction.java @@ -3,10 +3,8 @@ package com.intellij.updater; import java.io.DataInputStream; import java.io.File; import java.io.IOException; -import java.util.zip.ZipFile; -import java.util.zip.ZipOutputStream; -public class DeleteAction extends PatchAction { +public class DeleteAction extends BaseDeleteAction { public DeleteAction(String path, long checksum) { super(path, checksum); } @@ -16,54 +14,7 @@ public class DeleteAction extends PatchAction { } @Override - public void doBuildPatchFile(File olderFile, File newerFile, ZipOutputStream patchOutput) throws IOException { - // do nothing - } - - @Override - protected ValidationResult doValidate(File toFile) throws IOException { - ValidationResult result = doValidateAccess(toFile, ValidationResult.Action.DELETE); - if (result != null) return result; - - if (toFile.exists() && isModified(toFile)) { - return new ValidationResult(ValidationResult.Kind.CONFLICT, - myPath, - ValidationResult.Action.DELETE, - "Modified", - ValidationResult.Option.DELETE, - ValidationResult.Option.KEEP); - } - return null; - } - - @Override - protected boolean shouldApplyOn(File toFile) { - return toFile.exists(); - } - - @Override - protected void doApply(ZipFile patchFile, File toFile) throws IOException { - try { - Utils.delete(toFile); - } catch (IOException e) { - if (Utils.isWindows() && toFile.exists()) { - throw new RetryException(e); - } else { - throw e; - } - } - } - - @Override - protected void doBackup(File toFile, File backupFile) throws IOException { - Utils.copy(toFile, backupFile); - } - - @Override - protected void doRevert(File toFile, File backupFile) throws IOException { - if (!toFile.exists() || isModified(toFile)) { - Utils.delete(toFile); // make sure there is no directory remained on this path (may remain from previous 'create' actions - Utils.copy(backupFile, toFile); - } + protected boolean isModified(File toFile) throws IOException { + return myChecksum != Digester.digestRegularFile(toFile); } } diff --git a/updater/src/com/intellij/updater/DeleteZipAction.java b/updater/src/com/intellij/updater/DeleteZipAction.java new file mode 100644 index 000000000000..0cd46d9fa9e5 --- /dev/null +++ b/updater/src/com/intellij/updater/DeleteZipAction.java @@ -0,0 +1,20 @@ +package com.intellij.updater; + +import java.io.DataInputStream; +import java.io.File; +import java.io.IOException; + +public class DeleteZipAction extends BaseDeleteAction { + public DeleteZipAction(String path, long checksum) { + super(path, checksum); + } + + public DeleteZipAction(DataInputStream in) throws IOException { + super(in); + } + + @Override + protected boolean isModified(File toFile) throws IOException { + return myChecksum != Digester.digestFile(toFile); + } +} diff --git a/updater/src/com/intellij/updater/Digester.java b/updater/src/com/intellij/updater/Digester.java index ba6a4703e241..b84c33e3f6d5 100644 --- a/updater/src/com/intellij/updater/Digester.java +++ b/updater/src/com/intellij/updater/Digester.java @@ -22,15 +22,14 @@ public class Digester { } public static long digestFile(File file) throws IOException { - if (Utils.isZipFile(file.getName())) { + if (!Runner.ZIP_AS_BINARY && Utils.isZipFile(file.getName())) { ZipFile zipFile; try { zipFile = new ZipFile(file); } catch (IOException e) { - // If this isn't a zip file, this isn't really an error, merely an info. - Runner.infoStackTrace("Can't open file as zip file: " + file.getPath() + "\n", e); - return doDigestRegularFile(file); + Runner.printStackTrace(e); + return digestRegularFile(file); } try { @@ -40,10 +39,10 @@ public class Digester { zipFile.close(); } } - return doDigestRegularFile(file); + return digestRegularFile(file); } - private static long doDigestRegularFile(File file) throws IOException { + public static long digestRegularFile(File file) throws IOException { InputStream in = new BufferedInputStream(new FileInputStream(file)); try { return digestStream(in); diff --git a/updater/src/com/intellij/updater/NativeFileManager.java b/updater/src/com/intellij/updater/NativeFileManager.java new file mode 100644 index 000000000000..b61c1a7c35a1 --- /dev/null +++ b/updater/src/com/intellij/updater/NativeFileManager.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * 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.updater; + +import com.sun.jna.Pointer; +import com.sun.jna.StringArray; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Kernel32; +import com.sun.jna.platform.win32.WinBase; +import com.sun.jna.platform.win32.WinNT; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.LongByReference; + +import java.io.File; +import java.util.LinkedList; +import java.util.List; + +/** + * A utility class to find processes that hold a lock to a file. This relies on a Windows API called + * RestartManager {@see http://msdn.microsoft.com/en-us/library/windows/desktop/cc948910(v=vs.85).aspx} + * + * This class uses the RestartManager and the Kernel32 APIs, and it tries to initialize them the first + * time it is run. If the RestartManager DLL is not found, it being because we are running on XP or + * because we are not running on Windows, then the class is flagged as failed and no further attempts + * will be made to load the DLL. + */ +public class NativeFileManager { + + private static final int MAX_PROCESSES = 10; + + private static boolean ourFailed = false; + + public static class Process { + public final int pid; + public final String name; + + public Process(int pid, String name) { + this.pid = pid; + this.name = name; + } + + public boolean terminate() { + Kernel32.HANDLE process = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_TERMINATE | WinNT.SYNCHRONIZE, false, pid); + if (process.getPointer() == null) { + Runner.logger.warn("Unable to find process " + name + "(" + pid + ")"); + return false; + } else { + Kernel32.INSTANCE.TerminateProcess(process, 1); + int wait = Kernel32.INSTANCE.WaitForSingleObject(process, 1000); + if (wait != WinBase.WAIT_OBJECT_0) { + Runner.logger.warn("Timed out while waiting for process " + name + "(" + pid + ") to end"); + return false; + } + Kernel32.INSTANCE.CloseHandle(process); + return true; + } + } + } + + public static List getProcessesUsing(File file) { + List processes = new LinkedList(); + // If the DLL was not present (XP or other OS), do not try to find it again. + if (ourFailed) { + return processes; + } + try { + IntByReference session = new IntByReference(); + char[] sessionKey = new char[Win32RestartManager.CCH_RM_SESSION_KEY + 1]; + int error = Win32RestartManager.INSTANCE.RmStartSession(session, 0, sessionKey); + if (error != 0) { + Runner.logger.warn("Unable to start restart manager session"); + return processes; + } + StringArray resources = new StringArray(new WString[]{new WString(file.toString())}); + error = Win32RestartManager.INSTANCE.RmRegisterResources(session.getValue(), 1, resources, 0, Pointer.NULL, 0, null); + if (error != 0) { + Runner.logger.warn("Unable to register restart manager resource " + file.getAbsolutePath()); + return processes; + } + + IntByReference procInfoNeeded = new IntByReference(); + Win32RestartManager.RmProcessInfo info = new Win32RestartManager.RmProcessInfo(); + Win32RestartManager.RmProcessInfo[] infos = (Win32RestartManager.RmProcessInfo[])info.toArray(MAX_PROCESSES); + IntByReference procInfo = new IntByReference(infos.length); + error = Win32RestartManager.INSTANCE.RmGetList(session.getValue(), procInfoNeeded, procInfo, info, new LongByReference()); + if (error != 0) { + Runner.logger.warn("Unable to get the list of processes using " + file.getAbsolutePath()); + return processes; + } + for (int i = 0; i < procInfo.getValue(); i++) { + processes.add(new Process(infos[i].Process.dwProcessId, new String(infos[i].strAppName).trim())); + } + Win32RestartManager.INSTANCE.RmEndSession(session.getValue()); + } catch (Throwable t) { + // Best effort approach, if no DLL is found ignore. + ourFailed = true; + } + return processes; + } +} diff --git a/updater/src/com/intellij/updater/Patch.java b/updater/src/com/intellij/updater/Patch.java index 9b66535b2449..d9a33a6df24d 100644 --- a/updater/src/com/intellij/updater/Patch.java +++ b/updater/src/com/intellij/updater/Patch.java @@ -11,6 +11,7 @@ public class Patch { private static final int UPDATE_ACTION_KEY = 2; private static final int UPDATE_ZIP_ACTION_KEY = 3; private static final int DELETE_ACTION_KEY = 4; + private static final int DELETE_ZIP_ACTION_KEY = 5; public Patch(File olderDir, File newerDir, @@ -45,7 +46,12 @@ public class Patch { // 'delete' actions before 'create' actions to prevent newly created files to be deleted if the names differ only on case. for (Map.Entry each : diff.filesToDelete.entrySet()) { - tempActions.add(new DeleteAction(each.getKey(), each.getValue())); + if (!Runner.ZIP_AS_BINARY && Utils.isZipFile(each.getKey())) { + tempActions.add(new DeleteZipAction(each.getKey(), each.getValue())); + } else + { + tempActions.add(new DeleteAction(each.getKey(), each.getValue())); + } } for (String each : diff.filesToCreate) { @@ -53,7 +59,7 @@ public class Patch { } for (Map.Entry each : diff.filesToUpdate.entrySet()) { - if (Utils.isZipFile(each.getKey())) { + if (!Runner.ZIP_AS_BINARY && Utils.isZipFile(each.getKey())) { tempActions.add(new UpdateZipAction(each.getKey(), each.getValue())); } else { @@ -98,6 +104,9 @@ public class Patch { else if (clazz == UpdateZipAction.class) { key = UPDATE_ZIP_ACTION_KEY; } + else if (clazz == DeleteZipAction.class) { + key = DELETE_ZIP_ACTION_KEY; + } else if (clazz == DeleteAction.class) { key = DELETE_ACTION_KEY; } @@ -135,6 +144,9 @@ public class Patch { case DELETE_ACTION_KEY: a = new DeleteAction(in); break; + case DELETE_ZIP_ACTION_KEY: + a = new DeleteZipAction(in); + break; default: throw new RuntimeException("Unknown action type " + key); } diff --git a/updater/src/com/intellij/updater/PatchAction.java b/updater/src/com/intellij/updater/PatchAction.java index 42afb50b989d..e91330eae3bd 100644 --- a/updater/src/com/intellij/updater/PatchAction.java +++ b/updater/src/com/intellij/updater/PatchAction.java @@ -4,6 +4,8 @@ import java.io.*; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.channels.OverlappingFileLockException; +import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; @@ -60,9 +62,15 @@ public abstract class PatchAction { protected abstract void doBuildPatchFile(File olderFile, File newerFile, ZipOutputStream patchOutput) throws IOException; public boolean shouldApply(File toDir, Map options) { + File file = getFile(toDir); ValidationResult.Option option = options.get(myPath); if (option == ValidationResult.Option.KEEP || option == ValidationResult.Option.IGNORE) return false; - return shouldApplyOn(getFile(toDir)); + if (option == ValidationResult.Option.KILL_PROCESS) { + for (NativeFileManager.Process process : NativeFileManager.getProcessesUsing(file)) { + process.terminate(); + } + } + return shouldApplyOn(file); } protected boolean shouldApplyOn(File toFile) { @@ -78,6 +86,10 @@ public abstract class PatchAction { protected ValidationResult doValidateAccess(File toFile, ValidationResult.Action action) { if (!toFile.exists()) return null; if (toFile.isDirectory()) return null; + ValidationResult result = validateProcessLock(toFile, action); + if (result != null) { + return result; + } if (toFile.canRead() && toFile.canWrite() && isWritable(toFile)) return null; return new ValidationResult(ValidationResult.Kind.ERROR, myPath, @@ -111,6 +123,23 @@ public abstract class PatchAction { } } + private ValidationResult validateProcessLock(File toFile, ValidationResult.Action action) { + List processes = NativeFileManager.getProcessesUsing(toFile); + if (processes.size() > 0) { + Iterator it = processes.iterator(); + String message = "Locked by: " + it.next().name; + while (it.hasNext()) { + message += ", " + it.next().name; + } + return new ValidationResult(ValidationResult.Kind.ERROR, + myPath, + action, + message, + ValidationResult.Option.KILL_PROCESS); + } + return null; + } + protected ValidationResult doValidateNotChanged(File toFile, ValidationResult.Kind kind, ValidationResult.Action action) throws IOException { if (toFile.exists()) { @@ -132,9 +161,7 @@ public abstract class PatchAction { return null; } - protected boolean isModified(File toFile) throws IOException { - return myChecksum != Digester.digestFile(toFile); - } + abstract protected boolean isModified(File toFile) throws IOException; public void apply(ZipFile patchFile, File toDir) throws IOException { doApply(patchFile, getFile(toDir)); diff --git a/updater/src/com/intellij/updater/RetryException.java b/updater/src/com/intellij/updater/RetryException.java deleted file mode 100644 index e13c4eb0f2c8..000000000000 --- a/updater/src/com/intellij/updater/RetryException.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * 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.updater; - -import java.io.IOException; - -/** - * Exception thrown when an IOException arises when performing a patch - * action and it's likely that retrying will be successful. - */ -public class RetryException extends IOException { - public RetryException() { - } - - public RetryException(String message) { - super(message); - } - - public RetryException(String message, Throwable cause) { - super(message, cause); - } - - public RetryException(Throwable cause) { - super(cause); - } -} diff --git a/updater/src/com/intellij/updater/Runner.java b/updater/src/com/intellij/updater/Runner.java index 56a331b5ebc8..354dba99713a 100644 --- a/updater/src/com/intellij/updater/Runner.java +++ b/updater/src/com/intellij/updater/Runner.java @@ -18,6 +18,13 @@ import java.util.zip.ZipInputStream; public class Runner { public static Logger logger = null; + /** + * Treats zip files as regular binary files. When false, zip/jar files are unzipped and diffed file by file. + * When true, the entire zip file is diffed as a single file. Set to true if preserving the timestamps of + * the files inside the zip is important. This variable can change via a command line option. + */ + public static boolean ZIP_AS_BINARY = false; + private static final String PATCH_FILE_NAME = "patch-file.zip"; private static final String PATCH_PROPERTIES_ENTRY = "patch.properties"; private static final String OLD_BUILD_DESCRIPTION = "old.build.description"; @@ -32,28 +39,19 @@ public class Runner { String patchFile = args[5]; initLogger(); + ZIP_AS_BINARY = Arrays.asList(args).contains("--zip_as_binary"); + List ignoredFiles = extractFiles(args, "ignored"); List criticalFiles = extractFiles(args, "critical"); List optionalFiles = extractFiles(args, "optional"); create(oldVersionDesc, newVersionDesc, oldFolder, newFolder, patchFile, ignoredFiles, criticalFiles, optionalFiles); } else if (args.length >= 2 && "install".equals(args[0])) { - // install [--exit0] - int nextArg = 1; - - // Default install exit code is SwingUpdaterUI.RESULT_REQUIRES_RESTART (42) unless overridden to be 0. - // This is used by testUI/build.gradle as gradle expects a javaexec to exit with code 0. - boolean useExitCode0 = false; - if (args[nextArg].equals("--exit0")) { - useExitCode0 = true; - nextArg++; - } - - String destFolder = args[nextArg++]; + String destFolder = args[1]; initLogger(); logger.info("destFolder: " + destFolder); - install(useExitCode0, destFolder); + install(destFolder); } else { printUsage(); @@ -104,10 +102,6 @@ public class Runner { } } - public static void infoStackTrace(String msg, Throwable e){ - logger.info(msg, e); - } - public static void printStackTrace(Throwable e){ logger.error(e.getMessage(), e); } @@ -130,8 +124,8 @@ public class Runner { private static void printUsage() { System.err.println("Usage:\n" + "create " + - " [ignored=file1;file2;...] [critical=file1;file2;...] [optional=file1;file2;...]\n" + - "install [--exit0] [log_directory]\n"); + " [ignored=file1;file2;...] [critical=file1;file2;...] [optional=file1;file2;...]\n" + + "install \n"); } private static void create(String oldBuildDesc, @@ -142,31 +136,9 @@ public class Runner { List ignoredFiles, List criticalFiles, List optionalFiles) throws IOException, OperationCancelledException { - File tempPatchFile = Utils.createTempFile(); - createImpl(oldBuildDesc, - newBuildDesc, - oldFolder, - newFolder, - patchFile, - tempPatchFile, - ignoredFiles, - criticalFiles, - optionalFiles, - new ConsoleUpdaterUI(), resolveJarFile()); - } - - static void createImpl(String oldBuildDesc, - String newBuildDesc, - String oldFolder, - String newFolder, - String outPatchJar, - File tempPatchFile, - List ignoredFiles, - List criticalFiles, - List optionalFiles, - UpdaterUI ui, - File resolvedJar) throws IOException, OperationCancelledException { + UpdaterUI ui = new ConsoleUpdaterUI(); try { + File tempPatchFile = Utils.createTempFile(); PatchFileCreator.create(new File(oldFolder), new File(newFolder), tempPatchFile, @@ -175,13 +147,13 @@ public class Runner { optionalFiles, ui); - logger.info("Packing JAR file: " + outPatchJar ); - ui.startProcess("Packing JAR file '" + outPatchJar + "'..."); + logger.info("Packing JAR file: " + patchFile ); + ui.startProcess("Packing JAR file '" + patchFile + "'..."); - FileOutputStream fileOut = new FileOutputStream(outPatchJar); + FileOutputStream fileOut = new FileOutputStream(patchFile); try { ZipOutputWrapper out = new ZipOutputWrapper(fileOut); - ZipInputStream in = new ZipInputStream(new FileInputStream(resolvedJar)); + ZipInputStream in = new ZipInputStream(new FileInputStream(resolveJarFile())); try { ZipEntry e; while ((e = in.getNextEntry()) != null) { @@ -223,14 +195,16 @@ public class Runner { Utils.cleanup(); } - private static void install(final boolean useExitCode0, final String destFolder) throws Exception { + private static void install(final String destFolder) throws Exception { InputStream in = Runner.class.getResourceAsStream("/" + PATCH_PROPERTIES_ENTRY); Properties props = new Properties(); - try { - props.load(in); - } - finally { - in.close(); + if (in != null) { + try { + props.load(in); + } + finally { + in.close(); + } } // todo[r.sh] to delete in IDEA 14 (after a full circle of platform updates) @@ -250,9 +224,7 @@ public class Runner { new SwingUpdaterUI(props.getProperty(OLD_BUILD_DESCRIPTION), props.getProperty(NEW_BUILD_DESCRIPTION), - useExitCode0 ? 0 : SwingUpdaterUI.RESULT_REQUIRES_RESTART, new SwingUpdaterUI.InstallOperation() { - @Override public boolean execute(UpdaterUI ui) throws OperationCancelledException { logger.info("installing patch to the " + destFolder); return doInstall(ui, destFolder); @@ -260,26 +232,11 @@ public class Runner { }); } - interface IJarResolver { - File resolveJar() throws IOException; - } - private static boolean doInstall(UpdaterUI ui, String destFolder) throws OperationCancelledException { - return doInstallImpl(ui, destFolder, new IJarResolver() { - @Override - public File resolveJar() throws IOException { - return resolveJarFile(); - } - }); - } - - static boolean doInstallImpl(UpdaterUI ui, - String destFolder, - IJarResolver jarResolver) throws OperationCancelledException { try { try { File patchFile = Utils.createTempFile(); - ZipFile jarFile = new ZipFile(jarResolver.resolveJar()); + ZipFile jarFile = new ZipFile(resolveJarFile()); logger.info("Extracting patch file..."); ui.startProcess("Extracting patch file..."); @@ -325,6 +282,10 @@ public class Runner { } private static File resolveJarFile() throws IOException { + String jar = System.getProperty("JAR_FILE"); + if (jar != null) { + return new File(jar); + } URL url = Runner.class.getResource(""); if (url == null) throw new IOException("Cannot resolve JAR file path"); if (!"jar".equals(url.getProtocol())) throw new IOException("Patch file is not a JAR file"); diff --git a/updater/src/com/intellij/updater/SwingUpdaterUI.java b/updater/src/com/intellij/updater/SwingUpdaterUI.java index 749dcc2fc350..d44fc2e094f1 100644 --- a/updater/src/com/intellij/updater/SwingUpdaterUI.java +++ b/updater/src/com/intellij/updater/SwingUpdaterUI.java @@ -10,7 +10,6 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; -import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; @@ -20,7 +19,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicBoolean; public class SwingUpdaterUI implements UpdaterUI { - static final int RESULT_REQUIRES_RESTART = 42; + private static final int RESULT_REQUIRES_RESTART = 42; private static final EmptyBorder FRAME_BORDER = new EmptyBorder(8, 8, 8, 8); private static final EmptyBorder LABEL_BORDER = new EmptyBorder(0, 0, 5, 0); @@ -33,7 +32,6 @@ public class SwingUpdaterUI implements UpdaterUI { private static final String PROCEED_BUTTON_TITLE = "Proceed"; - private final int mySuccessExitCode; private final InstallOperation myOperation; private final JLabel myProcessTitle; @@ -42,30 +40,16 @@ public class SwingUpdaterUI implements UpdaterUI { private final JTextArea myConsole; private final JPanel myConsolePane; - private final JButton myRetryButton; private final JButton myCancelButton; private final ConcurrentLinkedQueue myQueue = new ConcurrentLinkedQueue(); private final AtomicBoolean isCancelled = new AtomicBoolean(false); private final AtomicBoolean isRunning = new AtomicBoolean(false); private final AtomicBoolean hasError = new AtomicBoolean(false); - private final AtomicBoolean hasRetry = new AtomicBoolean(false); private final JFrame myFrame; private boolean myApplied; - /** - * Displays the updater UI and asynchronously runs the operation list. - * - * @param oldBuildDesc The old build description, for display purposes. - * @param newBuildDesc The new build description, for display purposes. - * @param successExitCode The desired exit code on success. Default is {@link #RESULT_REQUIRES_RESTART}. - * @param operation The install operations to perform. - */ - public SwingUpdaterUI(String oldBuildDesc, - String newBuildDesc, - int successExitCode, - InstallOperation operation) { - mySuccessExitCode = successExitCode; + public SwingUpdaterUI(String oldBuildDesc, String newBuildDesc, InstallOperation operation) { myOperation = operation; myProcessTitle = new JLabel(" "); @@ -74,10 +58,6 @@ public class SwingUpdaterUI implements UpdaterUI { myCancelButton = new JButton(CANCEL_BUTTON_TITLE); - myRetryButton = new JButton("Retry"); - myRetryButton.setEnabled(false); - myRetryButton.setVisible(false); - myConsole = new JTextArea(); myConsole.setLineWrap(true); myConsole.setWrapStyleWord(true); @@ -90,19 +70,11 @@ public class SwingUpdaterUI implements UpdaterUI { myConsolePane.setVisible(false); myCancelButton.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent e) { doCancel(); } }); - myRetryButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - doRetry(); - } - }); - myFrame = new JFrame(); myFrame.setTitle(TITLE); @@ -132,7 +104,6 @@ public class SwingUpdaterUI implements UpdaterUI { buttonsPanel.setBorder(BUTTONS_BORDER); buttonsPanel.setLayout(new BoxLayout(buttonsPanel, BoxLayout.X_AXIS)); buttonsPanel.add(Box.createHorizontalGlue()); - buttonsPanel.add(myRetryButton); buttonsPanel.add(myCancelButton); myProcessTitle.setText("Updating " + oldBuildDesc + " to " + newBuildDesc + "..."); @@ -158,7 +129,6 @@ public class SwingUpdaterUI implements UpdaterUI { private void startRequestDispatching() { new Thread(new Runnable() { - @Override public void run() { while (true) { try { @@ -176,7 +146,6 @@ public class SwingUpdaterUI implements UpdaterUI { } SwingUtilities.invokeLater(new Runnable() { - @Override public void run() { for (UpdateRequest each : pendingRequests) { each.perform(); @@ -203,28 +172,10 @@ public class SwingUpdaterUI implements UpdaterUI { } } - private void doRetry() { - hasError.set(false); - hasRetry.set(false); - isCancelled.set(false); - myQueue.add(new UpdateRequest() { - @Override - public void perform() { - myConsole.setText(""); - myConsolePane.setVisible(false); - myConsolePane.setPreferredSize(new Dimension(10, 200)); - myRetryButton.setEnabled(false); - myCancelButton.setEnabled(true); - } - }); - doPerform(); - } - private void doPerform() { isRunning.set(true); new Thread(new Runnable() { - @Override public void run() { try { myApplied = myOperation.execute(SwingUpdaterUI.this); @@ -239,10 +190,6 @@ public class SwingUpdaterUI implements UpdaterUI { finally { isRunning.set(false); - if (hasRetry.get()) { - myRetryButton.setVisible(true); - myRetryButton.setEnabled(true); - } if (hasError.get()) { startProcess("Failed to apply patch"); setProgress(100); @@ -257,17 +204,15 @@ public class SwingUpdaterUI implements UpdaterUI { } private void exit() { - System.exit(myApplied ? mySuccessExitCode : 0); + System.exit(myApplied ? RESULT_REQUIRES_RESTART : 0); } - @Override public Map askUser(final List validationResults) throws OperationCancelledException { if (validationResults.isEmpty()) return Collections.emptyMap(); final Map result = new HashMap(); try { SwingUtilities.invokeAndWait(new Runnable() { - @Override public void run() { final JDialog dialog = new JDialog(myFrame, TITLE, true); dialog.setLayout(new BorderLayout()); @@ -279,7 +224,6 @@ public class SwingUpdaterUI implements UpdaterUI { buttonsPanel.add(Box.createHorizontalGlue()); JButton proceedButton = new JButton(PROCEED_BUTTON_TITLE); proceedButton.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent e) { dialog.setVisible(false); } @@ -287,7 +231,6 @@ public class SwingUpdaterUI implements UpdaterUI { JButton cancelButton = new JButton(CANCEL_BUTTON_TITLE); cancelButton.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent e) { isCancelled.set(true); myCancelButton.setEnabled(false); @@ -344,10 +287,8 @@ public class SwingUpdaterUI implements UpdaterUI { return result; } - @Override public void startProcess(final String title) { myQueue.add(new UpdateRequest() { - @Override public void perform() { myProcessStatus.setText(title); myProcessProgress.setIndeterminate(false); @@ -356,10 +297,8 @@ public class SwingUpdaterUI implements UpdaterUI { }); } - @Override public void setProgress(final int percentage) { myQueue.add(new UpdateRequest() { - @Override public void perform() { myProcessProgress.setIndeterminate(false); myProcessProgress.setValue(percentage); @@ -367,43 +306,21 @@ public class SwingUpdaterUI implements UpdaterUI { }); } - @Override public void setProgressIndeterminate() { myQueue.add(new UpdateRequest() { - @Override public void perform() { myProcessProgress.setIndeterminate(true); } }); } - @Override public void setStatus(final String status) { } - @Override public void showError(final Throwable e) { hasError.set(true); - StringWriter w = new StringWriter(); - - if (e instanceof RetryException) { - hasRetry.set(true); - - w.write("+----------------\n"); - w.write("| A file operation failed.\n"); - w.write("| This might be due to a file being locked by another\n"); - w.write("| application. Please try closing any application\n"); - w.write("| that uses the files being updated then press 'Retry'.\n"); - w.write("+----------------\n"); - w.write("\n\n"); - } - - e.printStackTrace(new PrintWriter(w)); - - final String content = w.getBuffer().toString(); myQueue.add(new UpdateRequest() { - @Override public void perform() { StringWriter w = new StringWriter(); if (!myConsolePane.isVisible()) { @@ -411,8 +328,9 @@ public class SwingUpdaterUI implements UpdaterUI { w.write(System.getProperty("java.io.tmpdir")); w.write("\n\n"); } + e.printStackTrace(new PrintWriter(w)); + w.append("\n"); myConsole.append(w.getBuffer().toString()); - myConsole.append(content); if (!myConsolePane.isVisible()) { myConsole.setCaretPosition(0); myConsolePane.setVisible(true); @@ -423,7 +341,6 @@ public class SwingUpdaterUI implements UpdaterUI { }); } - @Override public void checkCancelled() throws OperationCancelledException { if (isCancelled.get()) throw new OperationCancelledException(); } @@ -437,8 +354,7 @@ public class SwingUpdaterUI implements UpdaterUI { } public static void main(String[] args) { - new SwingUpdaterUI("xxx", "yyy", RESULT_REQUIRES_RESTART, new InstallOperation() { - @Override + new SwingUpdaterUI("xxx", "yyy", new InstallOperation() { public boolean execute(UpdaterUI ui) throws OperationCancelledException { ui.startProcess("Process1"); ui.checkCancelled(); @@ -522,7 +438,6 @@ public class SwingUpdaterUI implements UpdaterUI { } } - @Override public int getColumnCount() { return COLUMNS.length; } @@ -549,7 +464,6 @@ public class SwingUpdaterUI implements UpdaterUI { return super.getColumnClass(columnIndex); } - @Override public int getRowCount() { return myItems.size(); } @@ -566,7 +480,6 @@ public class SwingUpdaterUI implements UpdaterUI { } } - @Override public Object getValueAt(int rowIndex, int columnIndex) { Item item = myItems.get(rowIndex); switch (columnIndex) { diff --git a/updater/src/com/intellij/updater/UpdateAction.java b/updater/src/com/intellij/updater/UpdateAction.java index 6c79c44cbc4b..08cf7a188831 100644 --- a/updater/src/com/intellij/updater/UpdateAction.java +++ b/updater/src/com/intellij/updater/UpdateAction.java @@ -22,6 +22,11 @@ public class UpdateAction extends BaseUpdateAction { patchOutput.closeEntry(); } + @Override + protected boolean isModified(File toFile) throws IOException { + return myChecksum != Digester.digestRegularFile(toFile); + } + @Override protected void doApply(ZipFile patchFile, File toFile) throws IOException { InputStream in = Utils.findEntryInputStream(patchFile, myPath); diff --git a/updater/src/com/intellij/updater/UpdateZipAction.java b/updater/src/com/intellij/updater/UpdateZipAction.java index 7741b813318a..d808a735f0cd 100644 --- a/updater/src/com/intellij/updater/UpdateZipAction.java +++ b/updater/src/com/intellij/updater/UpdateZipAction.java @@ -153,6 +153,11 @@ public class UpdateZipAction extends BaseUpdateAction { } } + @Override + protected boolean isModified(File toFile) throws IOException { + return myChecksum != Digester.digestFile(toFile); + } + protected void doApply(final ZipFile patchFile, File toFile) throws IOException { File temp = Utils.createTempFile(); FileOutputStream fileOut = new FileOutputStream(temp); diff --git a/updater/src/com/intellij/updater/Utils.java b/updater/src/com/intellij/updater/Utils.java index b5ad212ac1a9..bce3333e106e 100644 --- a/updater/src/com/intellij/updater/Utils.java +++ b/updater/src/com/intellij/updater/Utils.java @@ -10,19 +10,10 @@ public class Utils { private static final byte[] BUFFER = new byte[64 * 1024]; private static File myTempDir; - public static boolean isWindows() { - return System.getProperty("os.name").startsWith("Windows"); - } - public static boolean isZipFile(String fileName) { return fileName.endsWith(".zip") || fileName.endsWith(".jar"); } - /** - * Creates a new temp file.
- * All the temp files created here are located in a unique root temp directory - * that is automatically deleted by {@link #cleanup()}. - */ @SuppressWarnings({"SSBasedInspection"}) public static File createTempFile() throws IOException { if (myTempDir == null) { @@ -35,12 +26,6 @@ public class Utils { return File.createTempFile("temp.", ".tmp", myTempDir); } - - /** - * Creates a new temp directory.
- * All the temp directories created here are located in a unique root temp directory - * that is automatically deleted by {@link #cleanup()}. - */ public static File createTempDir() throws IOException { File result = createTempFile(); delete(result); @@ -57,15 +42,6 @@ public class Utils { myTempDir = null; } - /** - * Deletes a file or directory with a default timeout of 100 milliseconds. - * Directories are deleted recursively. The timeout occurs on each file. - * If one of the files fails to be deleted, the recursive directory deletion - * is aborted and not retried. - * - * @param file The file or directory to delete. - * @throws IOException - */ public static void delete(File file) throws IOException { if (file.isDirectory()) { File[] files = file.listFiles(); @@ -76,20 +52,15 @@ public class Utils { } } } - for (int i = 0; i < 10; i++) { - if (file.delete() || !file.exists()) { - return; - } + if (file.delete() || !file.exists()) return; try { Thread.sleep(10); } catch (InterruptedException ignore) { Runner.printStackTrace(ignore); } } - if (file.exists()) { - throw new IOException("Cannot delete file " + file); - } + if (file.exists()) throw new IOException("Cannot delete file " + file); } public static void setExecutable(File file, boolean executable) throws IOException { diff --git a/updater/src/com/intellij/updater/ValidationResult.java b/updater/src/com/intellij/updater/ValidationResult.java index 02c142446152..6f56fd3bc0c3 100644 --- a/updater/src/com/intellij/updater/ValidationResult.java +++ b/updater/src/com/intellij/updater/ValidationResult.java @@ -24,7 +24,7 @@ public class ValidationResult implements Comparable { } public enum Option { - IGNORE, KEEP, REPLACE, DELETE + IGNORE, KEEP, REPLACE, DELETE, KILL_PROCESS } public static final String ABSENT_MESSAGE = "Absent"; diff --git a/updater/src/com/intellij/updater/Win32RestartManager.java b/updater/src/com/intellij/updater/Win32RestartManager.java new file mode 100644 index 000000000000..a502a57c0931 --- /dev/null +++ b/updater/src/com/intellij/updater/Win32RestartManager.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * 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.updater; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinBase; +import com.sun.jna.platform.win32.WinDef; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.LongByReference; + +public interface Win32RestartManager extends Library { + + Win32RestartManager INSTANCE = (Win32RestartManager) Native.loadLibrary("Rstrtmgr", Win32RestartManager.class); + + int CCH_RM_SESSION_KEY = 32; + int CCH_RM_MAX_APP_NAME = 255; + int CCH_RM_MAX_SVC_NAME = 63; + + class RmUniqueProcess extends Structure { + public int dwProcessId; + public WinBase.FILETIME ProcessStartTime; + } + + class RmProcessInfo extends Structure { + public RmUniqueProcess Process; + public char[] strAppName = new char[CCH_RM_MAX_APP_NAME + 1]; + public char[] strServiceShortName = new char[CCH_RM_MAX_SVC_NAME + 1]; + public int ApplicationType; + public WinDef.LONG AppStatus; + public int TSSessionId; + public boolean bRestartable; + } + + int RmGetList(int dwSessionHandle, + IntByReference pnProcInfoNeeded, + IntByReference pnProcInfo, + RmProcessInfo rgAffectedApps, + LongByReference lpdwRebootReasons); + + int RmStartSession(IntByReference pSessionHandle, + int dwSessionFlags, + char[] strSessionKey); + + int RmRegisterResources(int dwSessionHandle, + int nFiles, + StringArray rgsFilenames, + int nApplications, + Pointer rgApplications, + int nServices, + StringArray rgsServiceNames); + + int RmEndSession(int dwSessionHandle); +} \ No newline at end of file diff --git a/updater/testSrc/com/intellij/updater/PatchFileCreatorTest.java b/updater/testSrc/com/intellij/updater/PatchFileCreatorTest.java index 4e87db2a8eb0..2cffb224c60f 100644 --- a/updater/testSrc/com/intellij/updater/PatchFileCreatorTest.java +++ b/updater/testSrc/com/intellij/updater/PatchFileCreatorTest.java @@ -6,6 +6,7 @@ import org.junit.Test; import java.io.File; import java.io.IOException; +import java.io.RandomAccessFile; import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -94,6 +95,35 @@ public class PatchFileCreatorTest extends PatchTestCase { assertNothingHasChanged(preparationResult, new HashMap()); } + @Test + public void testRevertedWhenFileToDeleteIsProcessLocked() throws Exception { + if (!UtilsTest.mIsWindows) return; + + PatchFileCreator.create(myOlderDir, myNewerDir, myFile, Collections.emptyList(), Collections.emptyList(), + Collections.emptyList(), TEST_UI); + + + RandomAccessFile raf = new RandomAccessFile(new File(myOlderDir, "bin/idea.bat"),"rw"); + // Lock the file. FileLock is not good here, because we need to prevent deletion. + int b = raf.read(); + raf.seek(0); + raf.write(b); + + try { + PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); + + Map original = Digester.digestFiles(myOlderDir, Collections.emptyList(), TEST_UI); + + File backup = getTempFile("backup"); + PatchFileCreator.apply(preparationResult, new HashMap(), backup, TEST_UI); + + assertEquals(original, Digester.digestFiles(myOlderDir, Collections.emptyList(), TEST_UI)); + } + finally { + raf.close(); + } + } + @Test public void testApplyingWithAbsentFileToDelete() throws Exception { PatchFileCreator.create(myOlderDir, myNewerDir, myFile, Collections.emptyList(), Collections.emptyList(), @@ -307,6 +337,11 @@ public class PatchFileCreatorTest extends PatchTestCase { super("_dummy_file_", -1); } + @Override + protected boolean isModified(File toFile) throws IOException { + return false; + } + @Override protected void doBuildPatchFile(File olderFile, File newerFile, ZipOutputStream patchOutput) throws IOException { throw new UnsupportedOperationException(); diff --git a/updater/testSrc/com/intellij/updater/PatchTest.java b/updater/testSrc/com/intellij/updater/PatchTest.java index 14ff6c0abb18..b64ad9f51406 100644 --- a/updater/testSrc/com/intellij/updater/PatchTest.java +++ b/updater/testSrc/com/intellij/updater/PatchTest.java @@ -139,14 +139,16 @@ public class PatchTest extends PatchTestCase { try { FileLock lock = s.getChannel().lock(); try { + String message = UtilsTest.mIsWindows ? "Locked by: Java(TM) Platform SE binary" : ValidationResult.ACCESS_DENIED_MESSAGE; + ValidationResult.Option option = UtilsTest.mIsWindows ? ValidationResult.Option.KILL_PROCESS : ValidationResult.Option.IGNORE; List result = myPatch.validate(myOlderDir, TEST_UI); assertEquals( new HashSet(Arrays.asList( new ValidationResult(ValidationResult.Kind.ERROR, "Readme.txt", ValidationResult.Action.UPDATE, - ValidationResult.ACCESS_DENIED_MESSAGE, - ValidationResult.Option.IGNORE))), + message, + option))), new HashSet(result)); } finally { diff --git a/updater/testSrc/com/intellij/updater/UtilsTest.java b/updater/testSrc/com/intellij/updater/UtilsTest.java index 6e9e4ebf673b..ead979fd4055 100644 --- a/updater/testSrc/com/intellij/updater/UtilsTest.java +++ b/updater/testSrc/com/intellij/updater/UtilsTest.java @@ -23,12 +23,11 @@ import java.io.IOException; public class UtilsTest extends TestCase { - private boolean mIsWindows; + public static boolean mIsWindows = System.getProperty("os.name").startsWith("Windows"); @Override public void setUp() throws Exception { super.setUp(); - mIsWindows = Utils.isWindows(); } public void testDelete() throws Exception { diff --git a/updater/updater.iml b/updater/updater.iml index e55b58d17cd0..e05a4de5185c 100644 --- a/updater/updater.iml +++ b/updater/updater.iml @@ -11,6 +11,7 @@ + diff --git a/xml/impl/src/org/jetbrains/builtInWebServer/BuiltInServerOptions.java b/xml/impl/src/org/jetbrains/builtInWebServer/BuiltInServerOptions.java index 5eb65c1f9cbc..f6c2abf4f1e9 100644 --- a/xml/impl/src/org/jetbrains/builtInWebServer/BuiltInServerOptions.java +++ b/xml/impl/src/org/jetbrains/builtInWebServer/BuiltInServerOptions.java @@ -5,7 +5,6 @@ import com.intellij.notification.NotificationDisplayType; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; import com.intellij.openapi.application.ApplicationNamesInfo; -import com.intellij.openapi.application.PathManager; import com.intellij.openapi.components.*; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.SimpleConfigurable; @@ -20,18 +19,14 @@ import org.jetbrains.ide.BuiltInServerManager; import org.jetbrains.ide.CustomPortServerManager; import org.jetbrains.io.CustomPortServerManagerBase; -import java.io.File; import java.util.Collection; import java.util.Collections; @State( name = "BuiltInServerOptions", - storages = { - @Storage( - file = StoragePathMacros.APP_CONFIG + "/other.xml" - )} + storages = @Storage(file = StoragePathMacros.APP_CONFIG + "/other.xml") ) -public class BuiltInServerOptions implements PersistentStateComponent, ExportableComponent, Getter { +public class BuiltInServerOptions implements PersistentStateComponent, Getter { @Attribute public int builtInServerPort = 63342; @Attribute @@ -57,18 +52,6 @@ public class BuiltInServerOptions implements PersistentStateComponent