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 deleted file mode 100644 index 212ca37c6cae..000000000000 --- a/platform/platform-api/src/com/intellij/execution/util/ExecutableValidator.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright 2000-2010 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.util; - -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.openapi.application.ApplicationManager; -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 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. - * @author Kirill Likhodedov - */ -public abstract class ExecutableValidator { - - private Notification myNotification; - protected final Project myProject; - private final String myNotificationGroupId; - - 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; - } - - /** - * 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 - */ - public void setMessagesAndTitles(String notificationErrorTitle, String notificationErrorDescription, - String dialogTitle, String dialogDescription, String dialogErrorText, - String fileChooserTitle, String fileChooserDescription) { - 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. - */ - protected abstract String getCurrentExecutable(); - - /** - * Override this to save new (correct) executable path entered in the dialog. - * @param 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; - } - - /** - * Returns true if the supplied executable is valid. - * Default implementation: try to execute the given executable and test if output returned errors. - * @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) { - try { - GeneralCommandLine commandLine = new GeneralCommandLine(); - commandLine.setExePath(executable); - CapturingProcessHandler handler = new CapturingProcessHandler(commandLine.createProcess(), CharsetToolkit.getDefaultSystemCharset()); - ProcessOutput result = handler.runProcess(60 * 1000); - return !result.isTimeout() && (result.getExitCode() == 0) && result.getStderr().isEmpty(); - } catch (Throwable e) { - return false; - } - } - - 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() { - if (ApplicationManager.getApplication().isUnitTestMode() || ApplicationManager.getApplication().isHeadlessEnvironment()) { - return; - } - - final Notification newNotification = new Notification(myNotificationGroupId, myNotificationErrorTitle, - myNotificationErrorDescription, NotificationType.ERROR, - new NotificationListener() { - public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { - notificationHyperlinkUpdate(notification, event); - } - }); - - // 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() { - @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); - } - }); - } - - /** - * 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(); - } - } - } - - /** - * 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). - */ - public boolean checkExecutableAndNotifyIfNeeded() { - if (!isExecutableValid(getCurrentExecutable())) { - showExecutableNotConfiguredNotification(); - return false; - } - 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/platform/platform-impl/src/com/intellij/execution/ExecutableValidator.java b/platform/platform-impl/src/com/intellij/execution/ExecutableValidator.java new file mode 100644 index 000000000000..d7b771b31c97 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/execution/ExecutableValidator.java @@ -0,0 +1,147 @@ +/* + * 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. + * 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; + +import com.intellij.execution.configurations.GeneralCommandLine; +import com.intellij.execution.process.CapturingProcessHandler; +import com.intellij.execution.process.ProcessOutput; +import com.intellij.notification.*; +import com.intellij.openapi.application.ApplicationManager; +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.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. + * 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 { + + 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; + + /** + * 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 #showSettingsAndExpireIfFixed(com.intellij.notification.Notification)} + */ + public ExecutableValidator(Project project, String notificationErrorTitle, String notificationErrorDescription) { + myProject = project; + myNotificationErrorTitle = notificationErrorTitle; + myNotificationErrorDescription = notificationErrorDescription; + } + + /** + * @return path to current executable. + */ + protected abstract String getCurrentExecutable(); + + /** + * @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. + */ + @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. + */ + protected boolean isExecutableValid(String executable) { + try { + GeneralCommandLine commandLine = new GeneralCommandLine(); + commandLine.setExePath(executable); + CapturingProcessHandler handler = new CapturingProcessHandler(commandLine.createProcess(), CharsetToolkit.getDefaultSystemCharset()); + ProcessOutput result = handler.runProcess(60 * 1000); + return !result.isTimeout() && (result.getExitCode() == 0) && result.getStderr().isEmpty(); + } catch (Throwable e) { + return false; + } + } + + /** + * 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. + */ + private void showExecutableNotConfiguredNotification() { + if (ApplicationManager.getApplication().isUnitTestMode() || ApplicationManager.getApplication().isHeadlessEnvironment()) { + return; + } + + myNotificationGroup.createNotification("", prepareDescription(), NotificationType.ERROR, + new NotificationListener() { + public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { + showSettingsAndExpireIfFixed(notification); + } + } + ).notify(myProject.isDefault() ? null : myProject); + } + + @NotNull + private String prepareDescription() { + String executable = getCurrentExecutable(); + if (executable.isEmpty()) { + return String.format("%s%s", myNotificationErrorTitle, myNotificationErrorDescription); + } else { + return String.format("%s:%snull 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;
}
@@ -590,6 +590,7 @@ public class GitVcs extends AbstractVcs" + 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;
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;
- }
}
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/config/GitExecutableValidator.java b/plugins/git4idea/src/git4idea/config/GitExecutableValidator.java
index 964b4167182f..90fa45970ca5 100644
--- a/plugins/git4idea/src/git4idea/config/GitExecutableValidator.java
+++ b/plugins/git4idea/src/git4idea/config/GitExecutableValidator.java
@@ -15,16 +15,18 @@
*/
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;
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),
@@ -35,30 +37,25 @@ 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, @Nullable GitVcs vcs) {
+ super(project,
+ GitBundle.message("git.executable.notification.title"), GitBundle.message("git.executable.notification.description"));
+ myVcs = vcs;
}
@Override
protected String getCurrentExecutable() {
- return myVcs.getAppSettings().getPathToGit();
+ return GitVcsApplicationSettings.getInstance().getPathToGit();
+ }
+
+ @NotNull
+ @Override
+ protected Configurable getConfigurable() {
+ return myVcs.getConfigurable();
}
@Override
- protected Configurable getConfigurable(Project project) {
- return myVcs == null ? null : myVcs.getConfigurable();
- }
-
- @Override
- public boolean isExecutableValid(String executable) {
+ public boolean isExecutableValid(@NotNull String executable) {
try {
GeneralCommandLine commandLine = new GeneralCommandLine();
commandLine.setExePath(executable);
@@ -71,12 +68,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/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