From 42a8d45dd72209cc90abf750cdd4c8bd6c5ff2d0 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Wed, 5 Oct 2011 19:05:04 +0400 Subject: [PATCH 01/14] ExecutableValidator: don't show dialog, cleanup. * Remove ExecutableDialog - notification is to be shown on startup instead. Fix for IDEA-64352. * Move notification texts to the constructor. * Make non-used and non-overridden method private. * javadocs, @NotNull, @Nullable. * Git/HgExecutableValidator: don't query for Git/HgVcs.getInstance(), pass it in the constructor. --- .../execution/util/ExecutableDialog.form | 34 ----- .../execution/util/ExecutableDialog.java | 76 ---------- .../execution/util/ExecutableValidator.java | 135 ++++-------------- plugins/git4idea/src/git4idea/GitVcs.java | 4 +- .../config/GitExecutableValidator.java | 28 ++-- .../org/zmlx/hg4idea/HgVcsMessages.properties | 2 +- .../zmlx/hg4idea/HgExecutableValidator.java | 26 ++-- .../hg4idea/src/org/zmlx/hg4idea/HgVcs.java | 4 +- 8 files changed, 49 insertions(+), 260 deletions(-) delete mode 100644 platform/platform-api/src/com/intellij/execution/util/ExecutableDialog.form delete mode 100644 platform/platform-api/src/com/intellij/execution/util/ExecutableDialog.java diff --git a/platform/platform-api/src/com/intellij/execution/util/ExecutableDialog.form b/platform/platform-api/src/com/intellij/execution/util/ExecutableDialog.form deleted file mode 100644 index acb729e0796e..000000000000 --- a/platform/platform-api/src/com/intellij/execution/util/ExecutableDialog.form +++ /dev/null @@ -1,34 +0,0 @@ - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/platform/platform-api/src/com/intellij/execution/util/ExecutableDialog.java b/platform/platform-api/src/com/intellij/execution/util/ExecutableDialog.java deleted file mode 100644 index 82841fb99bd5..000000000000 --- a/platform/platform-api/src/com/intellij/execution/util/ExecutableDialog.java +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2008-2010 Victor Iacoban -// -// 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.util; - -import com.intellij.openapi.fileChooser.FileChooserDescriptor; -import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.ui.TextFieldWithBrowseButton; -import com.intellij.ui.DocumentAdapter; - -import javax.swing.*; -import javax.swing.event.DocumentEvent; - -public class ExecutableDialog extends DialogWrapper { - private JPanel myCenterPanel; - private TextFieldWithBrowseButton myExecutablePath; - private JLabel myInfoLabel; - private final ExecutableValidator myExecutableValidator; - private String myErrorNotValidText; - - public ExecutableDialog(Project project, ExecutableValidator executableValidator) { - super(project, false); - myExecutableValidator = executableValidator; - myErrorNotValidText = executableValidator.getDialogErrorText(); - init(); - updateUI(); - setTitle(myExecutableValidator.getDialogTitle()); - myInfoLabel.setText(myExecutableValidator.getDialogDescription()); - } - - @Override - protected JComponent createCenterPanel() { - return myCenterPanel; - } - - // disable OK button if current executable is not valid - private void updateUI() { - final String path = myExecutablePath.getText(); - if (myExecutableValidator.isExecutableValid(path)) { - setErrorText(null); - getOKAction().setEnabled(true); - } else { - setErrorText(myErrorNotValidText); - getOKAction().setEnabled(false); - } - } - - private void createUIComponents() { - myExecutablePath = new TextFieldWithBrowseButton(); - myExecutablePath.setText(myExecutableValidator.getCurrentExecutable()); - FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor(); - myExecutablePath.addBrowseFolderListener(myExecutableValidator.getFileChooserTitle(), myExecutableValidator.getFileChooserDescription(), - null, descriptor); - myExecutablePath.getTextField().getDocument().addDocumentListener(new DocumentAdapter() { - @Override - protected void textChanged(DocumentEvent e) { - updateUI(); - } - }); - } - - public String getPath() { - return myExecutablePath.getText(); - } -} \ No newline at end of file diff --git a/platform/platform-api/src/com/intellij/execution/util/ExecutableValidator.java b/platform/platform-api/src/com/intellij/execution/util/ExecutableValidator.java index 212ca37c6cae..258842a1bf2f 100644 --- a/platform/platform-api/src/com/intellij/execution/util/ExecutableValidator.java +++ b/platform/platform-api/src/com/intellij/execution/util/ExecutableValidator.java @@ -29,87 +29,56 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import javax.swing.event.HyperlinkEvent; /** - * Validates the supplied external executable. - * Shows notification or a dialog to fix it. + * Validates the given external executable. If it is not valid, shows notification to fix it. + * * @author Kirill Likhodedov */ public abstract class ExecutableValidator { - private Notification myNotification; - protected final Project myProject; + private final Project myProject; private final String myNotificationGroupId; + private final String myNotificationErrorTitle; + private final String myNotificationErrorDescription; - private String myNotificationErrorTitle = "Executable not valid"; - private String myNotificationErrorDescription = "You haven't configured a valid executable. Fix"; - private String myDialogTitle = "Executable"; - private String myDialogDescription = "Specify the full path to the executable"; - private String myDialogErrorText = "It doesn't appear to be a valid executable"; - private String myFileChooserTitle = "Executable"; - private String myFileChooserDescription = "Specify the full path to the executable"; - - public ExecutableValidator(Project project, String notificationGroupId) { - myProject = project; - myNotificationGroupId = notificationGroupId; - } + private Notification myNotification; /** * Configures notification and dialog by setting text messages and titles specific to the whoever uses the validator. * @param notificationErrorTitle title of the notification about not valid executable. * @param notificationErrorDescription description of this notification with a link to fix it (link action is defined by - * {@link #notificationHyperlinkUpdate(com.intellij.notification.Notification, javax.swing.event.HyperlinkEvent)} - * @param dialogTitle - * @param dialogDescription - * @param dialogErrorText - * @param fileChooserTitle + * {@link #showSettingsAndExpireIfFixed(com.intellij.notification.Notification)} */ - public void setMessagesAndTitles(String notificationErrorTitle, String notificationErrorDescription, - String dialogTitle, String dialogDescription, String dialogErrorText, - String fileChooserTitle, String fileChooserDescription) { + public ExecutableValidator(Project project, String notificationGroupId, String notificationErrorTitle, String notificationErrorDescription) { + myProject = project; + myNotificationGroupId = notificationGroupId; myNotificationErrorTitle = notificationErrorTitle; myNotificationErrorDescription = notificationErrorDescription; - myDialogTitle = dialogTitle; - myDialogDescription = dialogDescription; - myDialogErrorText = dialogErrorText; - myFileChooserTitle = fileChooserTitle; - myFileChooserDescription = fileChooserDescription; } /** - * Returns current executable persisted in the settings or elsewhere. - * @return Path to current executable. + * @return path to current executable. */ protected abstract String getCurrentExecutable(); /** - * Override this to save new (correct) executable path entered in the dialog. - * @param executable + * @return the settings configurable where the executable is shown and can be fixed. + * This configurable will be opened if user presses "Fix" on the notification about invalid executable. */ - protected void saveCurrentExecutable(String executable) { - } - - /** - * Returns the configurable page for the vcs containing settings for executable. - * This configurable will be opened if user presses "Fix" on the notification about invalid executable. - * May return null - in this case the settings dialog won't be displayed. - */ - @Nullable - protected Configurable getConfigurable(Project project) { - return null; - } + @NotNull + protected abstract Configurable getConfigurable(); /** * Returns true if the supplied executable is valid. * Default implementation: try to execute the given executable and test if output returned errors. + * This can take a long time since it spawns external process. * @param executable Path to executable. * @return true if process with the supplied executable completed without errors and with exit code 0. */ - @SuppressWarnings({"MethodMayBeStatic"}) - public boolean isExecutableValid(String executable) { + protected boolean isExecutableValid(String executable) { try { GeneralCommandLine commandLine = new GeneralCommandLine(); commandLine.setExePath(executable); @@ -121,36 +90,12 @@ public abstract class ExecutableValidator { } } - public boolean showDialog() { - final ExecutableDialog dialog = new ExecutableDialog(myProject, this); - UIUtil.invokeAndWaitIfNeeded(new Runnable() { - @Override - public void run() { - dialog.show(); - } - }); - if (dialog.isOK()) { - saveCurrentExecutable(dialog.getPath()); - return true; - } else { // user pressed cancel - showExecutableNotConfiguredNotification(); - return false; - } - } - - public boolean checkExecutableAndShowDialogIfNeeded() { - if (!isExecutableValid(getCurrentExecutable())) { - return showDialog(); - } - return true; - } - /** * Shows a notification about not configured executable with a link to the Settings to fix it. * Expires the notification if user fixes the path from the opened Settings dialog. * Makes sure that there is always only one notification about the problem in the stack of notifications. */ - public void showExecutableNotConfiguredNotification() { + private void showExecutableNotConfiguredNotification() { if (ApplicationManager.getApplication().isUnitTestMode() || ApplicationManager.getApplication().isHeadlessEnvironment()) { return; } @@ -159,7 +104,7 @@ public abstract class ExecutableValidator { myNotificationErrorDescription, NotificationType.ERROR, new NotificationListener() { public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { - notificationHyperlinkUpdate(notification, event); + showSettingsAndExpireIfFixed(notification); } }); @@ -178,26 +123,17 @@ public abstract class ExecutableValidator { }); } - /** - * The action which will be executed when the user presses "Fix" link in the notification description. - * By default it opens the Settings dialog on the page correspondent to the supplied configurable (e.g. Git configurable) - * and expires the notification after dialog is closed if executable was fixed in that dialog. - * One may override the method. - * Parameters are the same as in {@link com.intellij.notification.NotificationListener#hyperlinkUpdate(com.intellij.notification.Notification, javax.swing.event.HyperlinkEvent)} - */ - protected void notificationHyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { - Configurable configurable = getConfigurable(myProject); - if (configurable != null) { - ShowSettingsUtil.getInstance().showSettingsDialog(myProject, configurable); - if (isExecutableValid(getCurrentExecutable())) { - notification.expire(); - } + private void showSettingsAndExpireIfFixed(@NotNull Notification notification) { + Configurable configurable = getConfigurable(); + ShowSettingsUtil.getInstance().showSettingsDialog(myProject, configurable); + if (isExecutableValid(getCurrentExecutable())) { + notification.expire(); } } /** * Checks if executable is valid and displays the notification if not. - * @return true if executable was valid, false - if not valid (and notification is shown in that case). + * @return true if executable was valid, false - if not valid (and notification was shown in that case). */ public boolean checkExecutableAndNotifyIfNeeded() { if (!isExecutableValid(getCurrentExecutable())) { @@ -206,24 +142,5 @@ public abstract class ExecutableValidator { } return true; } - - String getDialogTitle() { - return myDialogTitle; - } - - String getDialogErrorText() { - return myDialogErrorText; - } - - String getFileChooserDescription() { - return myFileChooserDescription; - } - - String getFileChooserTitle() { - return myFileChooserTitle; - } - - public String getDialogDescription() { - return myDialogDescription; - } + } diff --git a/plugins/git4idea/src/git4idea/GitVcs.java b/plugins/git4idea/src/git4idea/GitVcs.java index 58add56336c8..97efaf81b333 100644 --- a/plugins/git4idea/src/git4idea/GitVcs.java +++ b/plugins/git4idea/src/git4idea/GitVcs.java @@ -345,10 +345,10 @@ public class GitVcs extends AbstractVcs { @Override protected void activate() { isActivated = true; - myExecutableValidator = new GitExecutableValidator(myProject); + myExecutableValidator = new GitExecutableValidator(myProject, this); if (!ApplicationManager.getApplication().isHeadlessEnvironment()) { - myExecutableValidator.checkExecutableAndShowDialogIfNeeded(); + myExecutableValidator.checkExecutableAndNotifyIfNeeded(); checkVersion(); } diff --git a/plugins/git4idea/src/git4idea/config/GitExecutableValidator.java b/plugins/git4idea/src/git4idea/config/GitExecutableValidator.java index 964b4167182f..4d288861050b 100644 --- a/plugins/git4idea/src/git4idea/config/GitExecutableValidator.java +++ b/plugins/git4idea/src/git4idea/config/GitExecutableValidator.java @@ -25,6 +25,7 @@ import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.CharsetToolkit; import git4idea.GitVcs; import git4idea.i18n.GitBundle; +import org.jetbrains.annotations.NotNull; /** * Project service that is used to check whether currently set git executable is valid (just calls 'git version' and parses the output), @@ -35,16 +36,10 @@ public class GitExecutableValidator extends ExecutableValidator { private GitVcs myVcs; - public GitExecutableValidator(Project project) { - super(project, GitVcs.NOTIFICATION_GROUP_ID.getDisplayId()); - myVcs = GitVcs.getInstance(project); - setMessagesAndTitles(GitBundle.message("git.executable.notification.title"), - GitBundle.message("git.executable.notification.description"), - GitBundle.message("git.executable.dialog.title"), - GitBundle.message("git.executable.dialog.description"), - GitBundle.message("git.executable.dialog.error"), - GitBundle.message("git.executable.filechooser.title"), - GitBundle.message("git.executable.filechooser.description")); + public GitExecutableValidator(@NotNull Project project, @NotNull GitVcs vcs) { + super(project, GitVcs.NOTIFICATION_GROUP_ID.getDisplayId(), + GitBundle.message("git.executable.notification.title"), GitBundle.message("git.executable.notification.description")); + myVcs = vcs; } @Override @@ -52,13 +47,14 @@ public class GitExecutableValidator extends ExecutableValidator { return myVcs.getAppSettings().getPathToGit(); } + @NotNull @Override - protected Configurable getConfigurable(Project project) { - return myVcs == null ? null : myVcs.getConfigurable(); + protected Configurable getConfigurable() { + return myVcs.getConfigurable(); } @Override - public boolean isExecutableValid(String executable) { + public boolean isExecutableValid(@NotNull String executable) { try { GeneralCommandLine commandLine = new GeneralCommandLine(); commandLine.setExePath(executable); @@ -71,12 +67,6 @@ public class GitExecutableValidator extends ExecutableValidator { } } - @Override - protected void saveCurrentExecutable(String executable) { - myVcs.getAppSettings().setPathToGit(executable); - myVcs.checkVersion(); - } - /** * Checks if git executable is valid. If not (which is a common case for low-level vcs exceptions), shows the * notification. Otherwise throws the exception. diff --git a/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties b/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties index b2f5d4db3c7c..a807164fb9e1 100644 --- a/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties +++ b/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties @@ -56,7 +56,7 @@ hg4idea.commit.partial.merge.title=Partial Merge Commit hg4idea.commit.error.messageEmpty=Please provide a commit message hg4idea.executable.notification.title=Mercurial executable is not valid -hg4idea.executable.notification.description=You haven't configured a valid executable. Mercurial integration won't work. Fix +hg4idea.executable.notification.description=You have to configure a valid hg executable to make Mercurial integration work. Fix hg4idea.executable.dialog.title=Mercurial executable hg4idea.executable.dialog.description=Specify the full path to hg executable hg4idea.executable.dialog.error=It doesn't appear to be a valid hg executable diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/HgExecutableValidator.java b/plugins/hg4idea/src/org/zmlx/hg4idea/HgExecutableValidator.java index c50b81e77488..3a092f7500bf 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/HgExecutableValidator.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/HgExecutableValidator.java @@ -15,22 +15,18 @@ package org.zmlx.hg4idea; import com.intellij.execution.util.ExecutableValidator; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; import org.zmlx.hg4idea.command.HgVersionCommand; public class HgExecutableValidator extends ExecutableValidator { private final HgVcs myVcs; - public HgExecutableValidator(Project project) { - super(project, HgVcs.NOTIFICATION_GROUP_ID); - myVcs = HgVcs.getInstance(project); - setMessagesAndTitles(HgVcsMessages.message("hg4idea.executable.notification.title"), - HgVcsMessages.message("hg4idea.executable.notification.description"), - HgVcsMessages.message("hg4idea.executable.dialog.title"), - HgVcsMessages.message("hg4idea.executable.dialog.description"), - HgVcsMessages.message("hg4idea.executable.dialog.error"), - HgVcsMessages.message("hg4idea.executable.filechooser.title"), - HgVcsMessages.message("hg4idea.executable.filechooser.description")); + public HgExecutableValidator(@NotNull Project project, @NotNull HgVcs vcs) { + super(project, HgVcs.NOTIFICATION_GROUP_ID, + HgVcsMessages.message("hg4idea.executable.notification.title"), + HgVcsMessages.message("hg4idea.executable.notification.description")); + myVcs = vcs; } @Override @@ -38,9 +34,10 @@ public class HgExecutableValidator extends ExecutableValidator { return myVcs.getHgExecutable(); } + @NotNull @Override - protected Configurable getConfigurable(Project project) { - return myVcs == null ? null : myVcs.getConfigurable(); + protected Configurable getConfigurable() { + return myVcs.getConfigurable(); } @Override @@ -48,9 +45,4 @@ public class HgExecutableValidator extends ExecutableValidator { return new HgVersionCommand().isValid(executable, myVcs.getGlobalSettings().isRunViaBash()); } - @Override - protected void saveCurrentExecutable(String executable) { - myVcs.getGlobalSettings().setHgExecutable(executable); - } - } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java b/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java index 92f8c79e66ec..cd9fb851ca2e 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java @@ -260,7 +260,7 @@ public class HgVcs extends AbstractVcs { public void activate() { // validate hg executable on start if (!ApplicationManager.getApplication().isUnitTestMode()) { - getExecutableValidator().checkExecutableAndShowDialogIfNeeded(); + getExecutableValidator().checkExecutableAndNotifyIfNeeded(); } // status bar @@ -372,7 +372,7 @@ public class HgVcs extends AbstractVcs { public HgExecutableValidator getExecutableValidator() { synchronized (myExecutableValidatorLock) { if (myExecutableValidator == null) { - myExecutableValidator = new HgExecutableValidator(myProject); + myExecutableValidator = new HgExecutableValidator(myProject, this); } return myExecutableValidator; } From 432a905f77cb9e8282139401c6812d4ea867e35b Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 6 Oct 2011 15:43:18 +0400 Subject: [PATCH 02/14] [Git] Don't show error in GitVcs.checkVersion if the problem is Git executable. --- plugins/git4idea/src/git4idea/GitVcs.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/plugins/git4idea/src/git4idea/GitVcs.java b/plugins/git4idea/src/git4idea/GitVcs.java index 97efaf81b333..ba87ab82e733 100644 --- a/plugins/git4idea/src/git4idea/GitVcs.java +++ b/plugins/git4idea/src/git4idea/GitVcs.java @@ -483,12 +483,14 @@ public class GitVcs extends AbstractVcs { VcsBalloonProblemNotifier.showOverVersionControlView(myProject, message, MessageType.ERROR); } } catch (Exception e) { - final String reason = (e.getCause() != null ? e.getCause() : e).getMessage(); - String message = GitBundle.message("vcs.unable.to.run.git", executable, reason); - if (!myProject.isDefault()) { - showMessage(message, ConsoleViewContentType.SYSTEM_OUTPUT.getAttributes()); + if (getExecutableValidator().checkExecutableAndNotifyIfNeeded()) { // check executable before notifying error + final String reason = (e.getCause() != null ? e.getCause() : e).getMessage(); + String message = GitBundle.message("vcs.unable.to.run.git", executable, reason); + if (!myProject.isDefault()) { + showMessage(message, ConsoleViewContentType.SYSTEM_OUTPUT.getAttributes()); + } + VcsBalloonProblemNotifier.showOverVersionControlView(myProject, message, MessageType.ERROR); } - VcsBalloonProblemNotifier.showOverVersionControlView(myProject, message, MessageType.ERROR); } } From fa022d01269222777b5009f05e456077b46c9b5d Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 6 Oct 2011 15:45:14 +0400 Subject: [PATCH 03/14] ExecutableValidator: register own sticky balloon NotificationGroup. Better notification text for Git/Hg, showing path to invalid executable. --- .../execution}/ExecutableValidator.java | 29 ++++++++++++------- .../config/GitExecutableValidator.java | 4 +-- .../src/git4idea/i18n/GitBundle.properties | 4 +-- .../org/zmlx/hg4idea/HgVcsMessages.properties | 9 ++---- .../zmlx/hg4idea/HgExecutableValidator.java | 4 +-- 5 files changed, 26 insertions(+), 24 deletions(-) rename platform/{platform-api/src/com/intellij/execution/util => platform-impl/src/com/intellij/execution}/ExecutableValidator.java (85%) diff --git a/platform/platform-api/src/com/intellij/execution/util/ExecutableValidator.java b/platform/platform-impl/src/com/intellij/execution/ExecutableValidator.java similarity index 85% rename from platform/platform-api/src/com/intellij/execution/util/ExecutableValidator.java rename to platform/platform-impl/src/com/intellij/execution/ExecutableValidator.java index 258842a1bf2f..0dbaf3644841 100644 --- a/platform/platform-api/src/com/intellij/execution/util/ExecutableValidator.java +++ b/platform/platform-impl/src/com/intellij/execution/ExecutableValidator.java @@ -13,15 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.execution.util; +package com.intellij.execution; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.process.CapturingProcessHandler; import com.intellij.execution.process.ProcessOutput; -import com.intellij.notification.Notification; -import com.intellij.notification.NotificationListener; -import com.intellij.notification.NotificationType; -import com.intellij.notification.Notifications; +import com.intellij.notification.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ShowSettingsUtil; @@ -38,9 +35,10 @@ import javax.swing.event.HyperlinkEvent; * @author Kirill Likhodedov */ public abstract class ExecutableValidator { + + private final NotificationGroup myNotificationGroup = new NotificationGroup("External Executable Critical Failures", NotificationDisplayType.STICKY_BALLOON, true); private final Project myProject; - private final String myNotificationGroupId; private final String myNotificationErrorTitle; private final String myNotificationErrorDescription; @@ -52,9 +50,8 @@ public abstract class ExecutableValidator { * @param notificationErrorDescription description of this notification with a link to fix it (link action is defined by * {@link #showSettingsAndExpireIfFixed(com.intellij.notification.Notification)} */ - public ExecutableValidator(Project project, String notificationGroupId, String notificationErrorTitle, String notificationErrorDescription) { + public ExecutableValidator(Project project, String notificationErrorTitle, String notificationErrorDescription) { myProject = project; - myNotificationGroupId = notificationGroupId; myNotificationErrorTitle = notificationErrorTitle; myNotificationErrorDescription = notificationErrorDescription; } @@ -100,8 +97,8 @@ public abstract class ExecutableValidator { return; } - final Notification newNotification = new Notification(myNotificationGroupId, myNotificationErrorTitle, - myNotificationErrorDescription, NotificationType.ERROR, + final String description = prepareDescription(); + final Notification newNotification = myNotificationGroup.createNotification("", description, NotificationType.ERROR, new NotificationListener() { public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { showSettingsAndExpireIfFixed(notification); @@ -110,7 +107,7 @@ public abstract class ExecutableValidator { // expire() needs to be called from AWT thread. // we also want to be sure that previous notification expires before new one is shown (and assigned to myNotification). - UIUtil.invokeLaterIfNeeded(new Runnable() { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { @Override public void run() { if (myNotification != null && !myNotification.isExpired()) { @@ -123,6 +120,16 @@ public abstract class ExecutableValidator { }); } + @NotNull + private String prepareDescription() { + String executable = getCurrentExecutable(); + if (executable.isEmpty()) { + return String.format("%s%s", myNotificationErrorTitle, myNotificationErrorDescription); + } else { + return String.format("%s: %s
%s", myNotificationErrorTitle, executable, myNotificationErrorDescription); + } + } + private void showSettingsAndExpireIfFixed(@NotNull Notification notification) { Configurable configurable = getConfigurable(); ShowSettingsUtil.getInstance().showSettingsDialog(myProject, configurable); diff --git a/plugins/git4idea/src/git4idea/config/GitExecutableValidator.java b/plugins/git4idea/src/git4idea/config/GitExecutableValidator.java index 4d288861050b..46bc78f90837 100644 --- a/plugins/git4idea/src/git4idea/config/GitExecutableValidator.java +++ b/plugins/git4idea/src/git4idea/config/GitExecutableValidator.java @@ -15,10 +15,10 @@ */ package git4idea.config; +import com.intellij.execution.ExecutableValidator; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.process.CapturingProcessHandler; import com.intellij.execution.process.ProcessOutput; -import com.intellij.execution.util.ExecutableValidator; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.VcsException; @@ -37,7 +37,7 @@ public class GitExecutableValidator extends ExecutableValidator { private GitVcs myVcs; public GitExecutableValidator(@NotNull Project project, @NotNull GitVcs vcs) { - super(project, GitVcs.NOTIFICATION_GROUP_ID.getDisplayId(), + super(project, GitBundle.message("git.executable.notification.title"), GitBundle.message("git.executable.notification.description")); myVcs = vcs; } diff --git a/plugins/git4idea/src/git4idea/i18n/GitBundle.properties b/plugins/git4idea/src/git4idea/i18n/GitBundle.properties index 8a981d534303..f1ab2d3961d7 100644 --- a/plugins/git4idea/src/git4idea/i18n/GitBundle.properties +++ b/plugins/git4idea/src/git4idea/i18n/GitBundle.properties @@ -495,8 +495,8 @@ executable.error.description = Git couldn't be started. Probably the path to Git git.commit.message.empty=Please specify commit message git.commit.message.empty.title=Commit message is empty -git.executable.notification.title=Git executable problem -git.executable.notification.description=Git couldn't be started. Probably the path to Git executable is not valid. Fix it. +git.executable.notification.title=Can't start Git +git.executable.notification.description=Probably the path to Git executable is not valid. Fix it. git.executable.dialog.title=Git executable git.executable.dialog.description=Specify the full path to Git executable git.executable.dialog.error=It doesn't appear to be a valid Git executable diff --git a/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties b/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties index a807164fb9e1..8fc64720308f 100644 --- a/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties +++ b/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties @@ -55,13 +55,8 @@ hg4idea.commit.partial.merge.message=Partial commits are not allowed durin hg4idea.commit.partial.merge.title=Partial Merge Commit hg4idea.commit.error.messageEmpty=Please provide a commit message -hg4idea.executable.notification.title=Mercurial executable is not valid -hg4idea.executable.notification.description=You have to configure a valid hg executable to make Mercurial integration work. Fix -hg4idea.executable.dialog.title=Mercurial executable -hg4idea.executable.dialog.description=Specify the full path to hg executable -hg4idea.executable.dialog.error=It doesn't appear to be a valid hg executable -hg4idea.executable.filechooser.title=Mercurial executable -hg4idea.executable.filechooser.description=Specify the full path to hg executable +hg4idea.executable.notification.title=Can't start Mercurial +hg4idea.executable.notification.description=Probably the path to hg executable is not valid. Fix it. hg4idea.init.dialog.title=Create Mercurial repository hg4idea.init.dialog.incorrect.path=The specified path is incorrect diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/HgExecutableValidator.java b/plugins/hg4idea/src/org/zmlx/hg4idea/HgExecutableValidator.java index 3a092f7500bf..54531ce2b2c3 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/HgExecutableValidator.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/HgExecutableValidator.java @@ -12,7 +12,7 @@ // limitations under the License. package org.zmlx.hg4idea; -import com.intellij.execution.util.ExecutableValidator; +import com.intellij.execution.ExecutableValidator; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; @@ -23,7 +23,7 @@ public class HgExecutableValidator extends ExecutableValidator { private final HgVcs myVcs; public HgExecutableValidator(@NotNull Project project, @NotNull HgVcs vcs) { - super(project, HgVcs.NOTIFICATION_GROUP_ID, + super(project, HgVcsMessages.message("hg4idea.executable.notification.title"), HgVcsMessages.message("hg4idea.executable.notification.description")); myVcs = vcs; From 17332b6bb3ab36789ff841e3bcd04a5a98092540 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Mon, 24 Oct 2011 17:41:30 +0400 Subject: [PATCH 04/14] unused method --- .../src/git4idea/checkout/GitCloneDialog.java | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/plugins/git4idea/src/git4idea/checkout/GitCloneDialog.java b/plugins/git4idea/src/git4idea/checkout/GitCloneDialog.java index ac7336319f15..b90d1e47f8d2 100644 --- a/plugins/git4idea/src/git4idea/checkout/GitCloneDialog.java +++ b/plugins/git4idea/src/git4idea/checkout/GitCloneDialog.java @@ -325,19 +325,4 @@ public class GitCloneDialog extends DialogWrapper { return "reference.VersionControl.Git.CloneRepository"; } - /** - * Prepare check repository handler. To do this ls-remote command is executed and attempts to match - * master tag. This will likely return only single entry or none, if there is no master - * branch. Stdout output is ignored. Stderr is used to construct exception message and shown - * in error message box if exit is negative. - * - * @param project the project - * @param url the url to check - * @return a simple handler that does the task - */ - private static GitSimpleHandler checkRepository(Project project, final String url) { - GitSimpleHandler handler = new GitSimpleHandler(project, new File("."), GitCommand.LS_REMOTE); - handler.addParameters(url, "master"); - return handler; - } } From a236b622d595810921b54e74d9ec1f7d3fb987c2 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Mon, 24 Oct 2011 17:59:44 +0400 Subject: [PATCH 05/14] Git.init(): use GitSimpleHandler for correct error reporting --- plugins/git4idea/src/git4idea/Git.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/git4idea/src/git4idea/Git.java b/plugins/git4idea/src/git4idea/Git.java index d226afd87349..cd43843af5b0 100644 --- a/plugins/git4idea/src/git4idea/Git.java +++ b/plugins/git4idea/src/git4idea/Git.java @@ -49,9 +49,9 @@ public class Git { * // TODO use common format */ public static void init(Project project, VirtualFile root) throws VcsException { - GitLineHandler h = new GitLineHandler(project, root, GitCommand.INIT); + GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.INIT); h.setNoSSH(true); - GitHandlerUtil.runInCurrentThread(h, null); + h.run(); if (!h.errors().isEmpty()) { throw h.errors().get(0); } From 60f7632b1ce91a734b3cc876d282c374430c0867 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Mon, 24 Oct 2011 18:09:14 +0400 Subject: [PATCH 06/14] GitExecutableValidator: get path from GitVcsApplicationSettings, let vcs be null (although it doesn't save from npe in getConfigurable()) --- .../git4idea/src/git4idea/config/GitExecutableValidator.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/git4idea/src/git4idea/config/GitExecutableValidator.java b/plugins/git4idea/src/git4idea/config/GitExecutableValidator.java index 46bc78f90837..90fa45970ca5 100644 --- a/plugins/git4idea/src/git4idea/config/GitExecutableValidator.java +++ b/plugins/git4idea/src/git4idea/config/GitExecutableValidator.java @@ -26,6 +26,7 @@ import com.intellij.openapi.vfs.CharsetToolkit; import git4idea.GitVcs; import git4idea.i18n.GitBundle; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Project service that is used to check whether currently set git executable is valid (just calls 'git version' and parses the output), @@ -36,7 +37,7 @@ public class GitExecutableValidator extends ExecutableValidator { private GitVcs myVcs; - public GitExecutableValidator(@NotNull Project project, @NotNull GitVcs vcs) { + public GitExecutableValidator(@NotNull Project project, @Nullable GitVcs vcs) { super(project, GitBundle.message("git.executable.notification.title"), GitBundle.message("git.executable.notification.description")); myVcs = vcs; @@ -44,7 +45,7 @@ public class GitExecutableValidator extends ExecutableValidator { @Override protected String getCurrentExecutable() { - return myVcs.getAppSettings().getPathToGit(); + return GitVcsApplicationSettings.getInstance().getPathToGit(); } @NotNull From e3eb53d622b78f666a847470be7cce379b41bcc5 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Mon, 24 Oct 2011 18:22:12 +0400 Subject: [PATCH 07/14] @Nullable -> @NotNull --- .../src/git4idea/config/GitVcsApplicationSettings.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/git4idea/src/git4idea/config/GitVcsApplicationSettings.java b/plugins/git4idea/src/git4idea/config/GitVcsApplicationSettings.java index 22fb95a0a9c3..8fb5eecae11d 100644 --- a/plugins/git4idea/src/git4idea/config/GitVcsApplicationSettings.java +++ b/plugins/git4idea/src/git4idea/config/GitVcsApplicationSettings.java @@ -21,7 +21,7 @@ import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; import com.intellij.openapi.util.SystemInfo; import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.NotNull; import java.io.File; @@ -60,6 +60,7 @@ public class GitVcsApplicationSettings implements PersistentStateComponent Date: Thu, 27 Oct 2011 11:27:07 +0400 Subject: [PATCH 08/14] GitVersion is @NotNull now. Until initialized or if executable is invalid, it is a special constant GitVersion.NULL which is less that any other version. --- plugins/git4idea/src/git4idea/GitVcs.java | 9 ++--- .../src/git4idea/config/GitVersion.java | 35 +++++++++++++++---- .../SelectRepositoryAndShowLogAction.java | 8 ++--- .../git4idea/status/GitChangeProvider.java | 3 -- 4 files changed, 33 insertions(+), 22 deletions(-) diff --git a/plugins/git4idea/src/git4idea/GitVcs.java b/plugins/git4idea/src/git4idea/GitVcs.java index ba87ab82e733..2541a40d5dba 100644 --- a/plugins/git4idea/src/git4idea/GitVcs.java +++ b/plugins/git4idea/src/git4idea/GitVcs.java @@ -131,7 +131,7 @@ public class GitVcs extends AbstractVcs { private GitExecutableValidator myExecutableValidator; private GitBranchWidget myBranchWidget; - private GitVersion myVersion; // version of Git which this plugin uses. + private GitVersion myVersion = GitVersion.NULL; // version of Git which this plugin uses. @Nullable public static GitVcs getInstance(Project project) { @@ -495,13 +495,10 @@ public class GitVcs extends AbstractVcs { } /** - * @return the version number of Git, which is used by IDEA. null can be returned if the GitVcs hasn't yet activated. + * @return the version number of Git, which is used by IDEA. Or {@link GitVersion#NULL} if version info is unavailable yet. */ - @Nullable + @NotNull public GitVersion getVersion() { - if (myVersion == null) { - checkVersion(); - } return myVersion; } diff --git a/plugins/git4idea/src/git4idea/config/GitVersion.java b/plugins/git4idea/src/git4idea/config/GitVersion.java index f76a70b7af0d..99c5feb292c7 100644 --- a/plugins/git4idea/src/git4idea/config/GitVersion.java +++ b/plugins/git4idea/src/git4idea/config/GitVersion.java @@ -45,7 +45,13 @@ public final class GitVersion implements Comparable { * Type UNDEFINED means that the type doesn't matter in certain condition. */ public static enum Type { - UNDEFINED, UNIX, MSYS, CYGWIN + UNIX, + MSYS, + CYGWIN, + /** The type doesn't matter or couldn't be detected. */ + UNDEFINED, + /** Information about Git version is unavailable because the GitVcs hasn't fully initialized yet, or because Git executable is invalid. */ + NULL } /** @@ -53,13 +59,19 @@ public final class GitVersion implements Comparable { */ public static final GitVersion MIN = new GitVersion(1, 6, 0, 0); + /** + * Special version with a special Type which indicates, that Git version information is unavailable. + * Probably, because of invalid executable, or when GitVcs hasn't fully initialized yet. + */ + public static final GitVersion NULL = new GitVersion(0, 0, 0, 0, Type.NULL); + private static final Pattern FORMAT = Pattern.compile("git version (\\d+)\\.(\\d+)\\.(\\d+)(?:\\.(\\d+))?(?:\\.(msysgit))?[\\.\\-\\d\\w]*", Pattern.CASE_INSENSITIVE); private static final Logger LOG = Logger.getInstance(GitVersion.class.getName()); - private final int myMajor; // Major version number - private final int myMinor; // Minor version number - private final int myRevision; // Revision number - private final int myPatchLevel; // Patch level + private final int myMajor; + private final int myMinor; + private final int myRevision; + private final int myPatchLevel; private final Type myType; private final int myHashCode; @@ -145,7 +157,7 @@ public final class GitVersion implements Comparable { * @return true if the version is supported by the plugin */ public boolean isSupported() { - return compareTo(MIN) >= 0; + return getType() != Type.NULL && compareTo(MIN) >= 0; } /** @@ -183,8 +195,13 @@ public final class GitVersion implements Comparable { * (msys git 1.7.3).compareTo(cygwin git 1.7.3) == 0 * BUT * (msys git 1.7.3).equals(cygwin git 1.7.3) == false + * + * {@link GitVersion#NULL} is less than any other not-NULL version. */ - public int compareTo(final GitVersion o) { + public int compareTo(@NotNull GitVersion o) { + if (o.getType() == Type.NULL) { + return (getType() == Type.NULL ? 0 : 1); + } int d = myMajor - o.myMajor; if (d != 0) { return d; @@ -224,4 +241,8 @@ public final class GitVersion implements Comparable { return myType; } + public boolean isNull() { + return getType() == Type.NULL; + } + } diff --git a/plugins/git4idea/src/git4idea/history/wholeTree/SelectRepositoryAndShowLogAction.java b/plugins/git4idea/src/git4idea/history/wholeTree/SelectRepositoryAndShowLogAction.java index f4c1e83747bd..2f3c716f5bdf 100644 --- a/plugins/git4idea/src/git4idea/history/wholeTree/SelectRepositoryAndShowLogAction.java +++ b/plugins/git4idea/src/git4idea/history/wholeTree/SelectRepositoryAndShowLogAction.java @@ -233,7 +233,7 @@ public class SelectRepositoryAndShowLogAction extends AnAction { @Override public void onSuccess() { - if (myVersion.myVersion == null) return; + if (myVersion.myVersion.isNull()) return; if (myProject.isDisposed()) return; new MyDialog(myProject, myCorrectRoots).show(); } @@ -249,12 +249,8 @@ public class SelectRepositoryAndShowLogAction extends AnAction { @Override public void run(@NotNull ProgressIndicator indicator) { GitVcs vcs = GitVcs.getInstance(myProject); + if (vcs == null) { return; } myVersion = vcs.getVersion(); - if (myVersion == null) { - vcs.checkVersion(); - } - myVersion = vcs.getVersion(); - if (myVersion == null) return; } } diff --git a/plugins/git4idea/src/git4idea/status/GitChangeProvider.java b/plugins/git4idea/src/git4idea/status/GitChangeProvider.java index 08d4a38d0dec..ae35fc82ad35 100644 --- a/plugins/git4idea/src/git4idea/status/GitChangeProvider.java +++ b/plugins/git4idea/src/git4idea/status/GitChangeProvider.java @@ -112,9 +112,6 @@ public class GitChangeProvider implements ChangeProvider { return false; } final GitVersion version = vcs.getVersion(); - if (version == null) { - return false; - } return GitVersionSpecialty.KNOWS_STATUS_PORCELAIN.existsIn(version); } From f55f1b5b023aeccbab73818c66ae931393bc682b Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 27 Oct 2011 12:31:55 +0400 Subject: [PATCH 09/14] EventLog: show content (till the first eol) along with "more" link for HTML-formatted text as for plane text. This is to show something besides just "more" in the log, if the title is empty and the body starts with an HTML tag. --- .../src/com/intellij/notification/EventLog.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/notification/EventLog.java b/platform/platform-impl/src/com/intellij/notification/EventLog.java index 608e1d9c386f..5e06faf317e9 100644 --- a/platform/platform-impl/src/com/intellij/notification/EventLog.java +++ b/platform/platform-impl/src/com/intellij/notification/EventLog.java @@ -127,12 +127,10 @@ public class EventLog implements Notifications { if (content.startsWith("<") && !content.startsWith(" Date: Thu, 27 Oct 2011 13:16:36 +0400 Subject: [PATCH 10/14] ExecutableValidator: better notifications. * Show notification above the toolwindow. Don't care about multiple notifications happening at once, since only one is shown anyway. * Check executable and notify in the case of failure on GitHandler.start(). * Don't report exception in case of invalid executable in GitHistoryProvider. --- .../execution/ExecutableValidator.java | 38 +++++++------------ plugins/git4idea/src/git4idea/GitVcs.java | 10 +++-- .../src/git4idea/commands/GitHandler.java | 1 + .../git4idea/history/GitHistoryProvider.java | 24 +++++++----- 4 files changed, 35 insertions(+), 38 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/execution/ExecutableValidator.java b/platform/platform-impl/src/com/intellij/execution/ExecutableValidator.java index 0dbaf3644841..930b9c937643 100644 --- a/platform/platform-impl/src/com/intellij/execution/ExecutableValidator.java +++ b/platform/platform-impl/src/com/intellij/execution/ExecutableValidator.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2010 JetBrains s.r.o. + * Copyright 2000-2011 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. @@ -24,26 +24,30 @@ import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ShowSettingsUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.CharsetToolkit; -import com.intellij.util.ui.UIUtil; +import com.intellij.openapi.wm.ToolWindowId; import org.jetbrains.annotations.NotNull; import javax.swing.event.HyperlinkEvent; /** - * Validates the given external executable. If it is not valid, shows notification to fix it. + * Validates the given external executable. + * If it is not valid, shows notification to fix it. + * Notification balloon in the upper right corner is shown only the first time. After it is shown in the toolwindow group. + * This is done, because it is hard to discover whether notification balloon is showing or not, and showing several balloons about a single + * problem is a bad idea. * * @author Kirill Likhodedov */ public abstract class ExecutableValidator { - - private final NotificationGroup myNotificationGroup = new NotificationGroup("External Executable Critical Failures", NotificationDisplayType.STICKY_BALLOON, true); + + public static final String NOTIFICATION_ID = "External Executable Critical Failures"; + private final NotificationGroup myNotificationGroup = new NotificationGroup(NOTIFICATION_ID, NotificationDisplayType.TOOL_WINDOW, true, + ToolWindowId.VCS); private final Project myProject; private final String myNotificationErrorTitle; private final String myNotificationErrorDescription; - private Notification myNotification; - /** * Configures notification and dialog by setting text messages and titles specific to the whoever uses the validator. * @param notificationErrorTitle title of the notification about not valid executable. @@ -97,29 +101,15 @@ public abstract class ExecutableValidator { return; } - final String description = prepareDescription(); - final Notification newNotification = myNotificationGroup.createNotification("", description, NotificationType.ERROR, + myNotificationGroup.createNotification("", prepareDescription(), NotificationType.ERROR, new NotificationListener() { public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { showSettingsAndExpireIfFixed(notification); } - }); - - // expire() needs to be called from AWT thread. - // we also want to be sure that previous notification expires before new one is shown (and assigned to myNotification). - UIUtil.invokeAndWaitIfNeeded(new Runnable() { - @Override - public void run() { - if (myNotification != null && !myNotification.isExpired()) { - // don't store this notification twice, but redisplay it again so that popup appears. - myNotification.expire(); - } - myNotification = newNotification; - Notifications.Bus.notify(myNotification, myProject.isDefault() ? null : myProject); } - }); + ).notify(myProject); } - + @NotNull private String prepareDescription() { String executable = getCurrentExecutable(); diff --git a/plugins/git4idea/src/git4idea/GitVcs.java b/plugins/git4idea/src/git4idea/GitVcs.java index 2541a40d5dba..2122fde8b779 100644 --- a/plugins/git4idea/src/git4idea/GitVcs.java +++ b/plugins/git4idea/src/git4idea/GitVcs.java @@ -128,7 +128,7 @@ public class GitVcs extends AbstractVcs { private final GitCommitAndPushExecutor myCommitAndPushExecutor; private GitReferenceTracker myReferenceTracker; private boolean isActivated; // If true, the vcs was activated - private GitExecutableValidator myExecutableValidator; + private final GitExecutableValidator myExecutableValidator; private GitBranchWidget myBranchWidget; private GitVersion myVersion = GitVersion.NULL; // version of Git which this plugin uses. @@ -169,6 +169,7 @@ public class GitVcs extends AbstractVcs { myCommitAndPushExecutor = new GitCommitAndPushExecutor(myCheckinEnvironment); myReferenceTracker = new GitReferenceTracker(myProject, this, myReferenceListeners.getMulticaster()); myTaskQueue = new BackgroundTaskQueue(myProject, GitBundle.getString("task.queue.title")); + myExecutableValidator = new GitExecutableValidator(myProject, this); } @@ -345,11 +346,11 @@ public class GitVcs extends AbstractVcs { @Override protected void activate() { isActivated = true; - myExecutableValidator = new GitExecutableValidator(myProject, this); if (!ApplicationManager.getApplication().isHeadlessEnvironment()) { - myExecutableValidator.checkExecutableAndNotifyIfNeeded(); - checkVersion(); + if (myExecutableValidator.checkExecutableAndNotifyIfNeeded()) { + checkVersion(); + } } if (!myProject.isDefault() && myRootTracker == null) { @@ -589,6 +590,7 @@ public class GitVcs extends AbstractVcs { return isActivated; } + @NotNull public GitExecutableValidator getExecutableValidator() { return myExecutableValidator; } diff --git a/plugins/git4idea/src/git4idea/commands/GitHandler.java b/plugins/git4idea/src/git4idea/commands/GitHandler.java index af0492298061..a5ed7bb705a5 100644 --- a/plugins/git4idea/src/git4idea/commands/GitHandler.java +++ b/plugins/git4idea/src/git4idea/commands/GitHandler.java @@ -412,6 +412,7 @@ public abstract class GitHandler { startHandlingStreams(); } catch (Throwable t) { + myVcs.getExecutableValidator().checkExecutableAndNotifyIfNeeded(); cleanupEnv(); myListeners.getMulticaster().startFailed(t); } diff --git a/plugins/git4idea/src/git4idea/history/GitHistoryProvider.java b/plugins/git4idea/src/git4idea/history/GitHistoryProvider.java index bdd0c763130a..3225167b10aa 100644 --- a/plugins/git4idea/src/git4idea/history/GitHistoryProvider.java +++ b/plugins/git4idea/src/git4idea/history/GitHistoryProvider.java @@ -183,17 +183,21 @@ public class GitHistoryProvider implements VcsHistoryProvider, VcsCacheableHisto final VcsAbstractHistorySession emptySession = createSession(path, Collections.emptyList(), null); partner.reportCreatedEmptySession(emptySession); final GitExecutableValidator validator = GitVcs.getInstance(myProject).getExecutableValidator(); - GitHistoryUtils.history(myProject, path, null, new Consumer() { - public void consume(GitFileRevision gitFileRevision) { - partner.acceptRevision(gitFileRevision); - } - }, new Consumer() { - public void consume(VcsException e) { - if (validator.checkExecutableAndNotifyIfNeeded()) { - partner.reportException(e); + try { + GitHistoryUtils.history(myProject, path, null, new Consumer() { + public void consume(GitFileRevision gitFileRevision) { + partner.acceptRevision(gitFileRevision); } - } - }); + }, new Consumer() { + public void consume(VcsException e) { + if (validator.checkExecutableAndNotifyIfNeeded()) { + partner.reportException(e); + } + } + }); + } catch (VcsException e) { + validator.showNotificationOrThrow(e); + } } /** From 7b77b5a5da51e4ef0a97e4409e72bb857eac83fb Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 27 Oct 2011 13:20:17 +0400 Subject: [PATCH 11/14] ExecutableValidator: make available for default project --- .../src/com/intellij/execution/ExecutableValidator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/execution/ExecutableValidator.java b/platform/platform-impl/src/com/intellij/execution/ExecutableValidator.java index 930b9c937643..5279609f2837 100644 --- a/platform/platform-impl/src/com/intellij/execution/ExecutableValidator.java +++ b/platform/platform-impl/src/com/intellij/execution/ExecutableValidator.java @@ -107,7 +107,7 @@ public abstract class ExecutableValidator { showSettingsAndExpireIfFixed(notification); } } - ).notify(myProject); + ).notify(myProject.isDefault() ? null : myProject); } @NotNull From 76b1157c7ee41e84a38b5c137470919ceaadd05b Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 27 Oct 2011 13:25:27 +0400 Subject: [PATCH 12/14] GitUiUtil.notifyMessages: make notifications available for default project --- plugins/git4idea/src/git4idea/ui/GitUIUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/git4idea/src/git4idea/ui/GitUIUtil.java b/plugins/git4idea/src/git4idea/ui/GitUIUtil.java index 80fc00866f2b..4d0532aadc27 100644 --- a/plugins/git4idea/src/git4idea/ui/GitUIUtil.java +++ b/plugins/git4idea/src/git4idea/ui/GitUIUtil.java @@ -66,7 +66,7 @@ public class GitUIUtil { desc += "
" + StringUtil.join(messages, "
"); } NotificationGroup group = important ? GitVcs.IMPORTANT_ERROR_NOTIFICATION : GitVcs.NOTIFICATION_GROUP_ID; - group.createNotification(title, desc, type, null).notify(project); + group.createNotification(title, desc, type, null).notify(project.isDefault() ? null : project); } public static void notifyMessage(Project project, @Nullable String title, @Nullable String description, NotificationType type, boolean important, @Nullable Collection errors) { From 668fb25e28d17f8c77dbc6edd67c7e732d2d5af5 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 27 Oct 2011 14:09:37 +0400 Subject: [PATCH 13/14] Git clone: cleanup if clone failed; don't double-notify about incorrect executable --- .../execution/ExecutableValidator.java | 4 +++ .../checkout/GitCheckoutProvider.java | 36 +++++++++++++------ 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/execution/ExecutableValidator.java b/platform/platform-impl/src/com/intellij/execution/ExecutableValidator.java index 5279609f2837..d7b771b31c97 100644 --- a/platform/platform-impl/src/com/intellij/execution/ExecutableValidator.java +++ b/platform/platform-impl/src/com/intellij/execution/ExecutableValidator.java @@ -140,4 +140,8 @@ public abstract class ExecutableValidator { return true; } + public boolean isExecutableValid() { + return isExecutableValid(getCurrentExecutable()); + } + } diff --git a/plugins/git4idea/src/git4idea/checkout/GitCheckoutProvider.java b/plugins/git4idea/src/git4idea/checkout/GitCheckoutProvider.java index 269e0c843a62..3e22ba9ea2b3 100644 --- a/plugins/git4idea/src/git4idea/checkout/GitCheckoutProvider.java +++ b/plugins/git4idea/src/git4idea/checkout/GitCheckoutProvider.java @@ -19,6 +19,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.CheckoutProvider; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; @@ -26,6 +27,7 @@ import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.vcsUtil.VcsUtil; import git4idea.Git; +import git4idea.GitVcs; import git4idea.actions.BasicAction; import git4idea.commands.GitCommand; import git4idea.commands.GitSimpleHandler; @@ -102,15 +104,27 @@ public class GitCheckoutProvider implements CheckoutProvider { } private static boolean doClone(ProgressIndicator indicator, Project project, String directoryName, String parentDirectory, String sourceRepositoryURL) { - final VirtualFile root = mkdir(project, directoryName, parentDirectory); - if (root == null) { return false; } - if (!init(project, root)) { return false; } - if (!addRemote(project, root, sourceRepositoryURL)) { return false; } - if (!fetch(project, root, indicator)) { return false; } - return checkout(project, root); + File dir = mkdir(project, directoryName, parentDirectory); + if (dir == null) { + return false; + } + VirtualFile root = VcsUtil.getVirtualFileWithRefresh(dir); + if (root != null && + init(project, root) && + addRemote(project, root, sourceRepositoryURL) && + fetch(project, root, indicator) && + checkout(project, root)) { + return true; + } + cleanup(dir); + return false; } - private static @Nullable VirtualFile mkdir(Project project, String directoryName, String parentDirectory) { + private static void cleanup(@NotNull File dir) { + FileUtil.delete(dir); + } + + private static @Nullable File mkdir(Project project, String directoryName, String parentDirectory) { final File dir = new File(parentDirectory, directoryName); if (dir.exists()) { GitUIUtil.notifyError(project, "Couldn't clone", "Directory " + dir + " already exists."); @@ -120,8 +134,7 @@ public class GitCheckoutProvider implements CheckoutProvider { GitUIUtil.notifyError(project, "Couldn't clone", "Can't create directory " + dir + ""); return null; } - - return VcsUtil.getVirtualFileWithRefresh(dir); + return dir; } private static boolean init(Project project, VirtualFile root) { @@ -129,7 +142,10 @@ public class GitCheckoutProvider implements CheckoutProvider { Git.init(project, root); } catch (VcsException e) { LOG.info("init ", e); - GitUIUtil.notifyError(project, "Couldn't clone", "Couldn't git init in " + root.getPresentableUrl() + "", true, e); + GitVcs vcs = GitVcs.getInstance(project); + if (vcs == null || vcs.getExecutableValidator().isExecutableValid()) { // invalid executable will be notified in GitHandler.start() + GitUIUtil.notifyError(project, "Couldn't clone", "Couldn't git init in " + root.getPresentableUrl() + "", true, e); + } return false; } return true; From 3e3d3fcef3377d975de0c94ca1df8b67fef4e4d3 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 27 Oct 2011 14:14:03 +0400 Subject: [PATCH 14/14] GitInit: don't show error if executable is invalid --- plugins/git4idea/src/git4idea/actions/GitInit.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/git4idea/src/git4idea/actions/GitInit.java b/plugins/git4idea/src/git4idea/actions/GitInit.java index 1dc8da6900f8..01d0cf5c8501 100644 --- a/plugins/git4idea/src/git4idea/actions/GitInit.java +++ b/plugins/git4idea/src/git4idea/actions/GitInit.java @@ -74,10 +74,13 @@ public class GitInit extends DumbAwareAction { } } + GitVcs vcs = GitVcs.getInstance(project); try { Git.init(project, root); } catch (VcsException ex) { - GitUIUtil.showOperationErrors(project, Collections.singleton(ex), "git init"); + if (vcs == null || vcs.getExecutableValidator().isExecutableValid()) { + GitUIUtil.showOperationErrors(project, Collections.singleton(ex), "git init"); + } return; } @@ -89,8 +92,7 @@ public class GitInit extends DumbAwareAction { } final String path = root.equals(baseDir) ? "" : root.getPath(); final Project finalProject = project; - GitVcs.getInstance(project).runInBackground(new Task.Backgroundable(finalProject, GitBundle.getString("common.refreshing")) { - + GitVcs.runInBackground(new Task.Backgroundable(finalProject, GitBundle.getString("common.refreshing")) { public void run(@NotNull ProgressIndicator indicator) { refreshAndConfigureVcsMappings(finalProject, root, path); }