mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-59345 Option to run Mercurial via 'bash -cl'
1. Added a checkbox to the HgConfigurationProjectPanel; note that in HgConfigurationProjectPanel.validate we take current value of the checkbox, because on may simultaneously change 2 values (radio button of hg executable and bash-option checkbox). 2. Added a flag to HgGlobalSettings and a passthrough method to HgProjectSettings. 3. Added a flag to the ShellCommand: executing in the 'bash -cl' mode. Note that we escape bash control characters (like |<$) and provide --login option to catch .bash_profile.
This commit is contained in:
@@ -39,7 +39,7 @@ public class HgExecutableValidator extends ExecutableValidator {
|
||||
|
||||
@Override
|
||||
public boolean isExecutableValid(String executable) {
|
||||
return new HgVersionCommand().isValid(executable);
|
||||
return new HgVersionCommand().isValid(executable, myVcs.getGlobalSettings().isRunViaBash());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
package org.zmlx.hg4idea;
|
||||
|
||||
import com.intellij.openapi.components.PersistentStateComponent;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
@@ -29,18 +28,32 @@ import java.util.Map;
|
||||
name = "HgGlobalSettings",
|
||||
storages = @Storage(id = "HgGlobalSettings", file = "$APP_CONFIG$/vcs.xml")
|
||||
)
|
||||
public class HgGlobalSettings implements PersistentStateComponent<HgGlobalSettings> {
|
||||
public class HgGlobalSettings implements PersistentStateComponent<HgGlobalSettings.State> {
|
||||
|
||||
private static final String HG = HgVcs.HG_EXECUTABLE_FILE_NAME;
|
||||
private static final int FIVE_MINUTES = 300;
|
||||
|
||||
private String hgExecutable = HG;
|
||||
private String myHgExecutable = HG;
|
||||
|
||||
// visited URL -> list of logins for this URL. Passwords are remembered in the PasswordSafe.
|
||||
private Map<String, List<String>> myRememberedUrls = new HashMap<String, List<String>>();
|
||||
private boolean myRunViaBash;
|
||||
|
||||
public static HgGlobalSettings getInstance() {
|
||||
return ServiceManager.getService(HgGlobalSettings.class);
|
||||
public static class State {
|
||||
public String hgExecutable;
|
||||
public boolean runViaBash;
|
||||
}
|
||||
|
||||
public State getState() {
|
||||
State s = new State();
|
||||
s.hgExecutable = myHgExecutable;
|
||||
s.runViaBash = myRunViaBash;
|
||||
return s;
|
||||
}
|
||||
|
||||
public void loadState(State state) {
|
||||
myHgExecutable = state.hgExecutable;
|
||||
myRunViaBash = state.runViaBash;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,31 +90,31 @@ public class HgGlobalSettings implements PersistentStateComponent<HgGlobalSettin
|
||||
}
|
||||
|
||||
public String getHgExecutable() {
|
||||
return hgExecutable;
|
||||
return myHgExecutable;
|
||||
}
|
||||
|
||||
public void setHgExecutable(String hgExecutable) {
|
||||
this.hgExecutable = hgExecutable;
|
||||
this.myHgExecutable = hgExecutable;
|
||||
}
|
||||
|
||||
public boolean isAutodetectHg() {
|
||||
return HG.equals(hgExecutable);
|
||||
return HG.equals(myHgExecutable);
|
||||
}
|
||||
|
||||
public void enableAutodetectHg() {
|
||||
hgExecutable = HG;
|
||||
myHgExecutable = HG;
|
||||
}
|
||||
|
||||
public static int getIncomingCheckIntervalSeconds() {
|
||||
return FIVE_MINUTES;
|
||||
}
|
||||
|
||||
public HgGlobalSettings getState() {
|
||||
return this;
|
||||
public boolean isRunViaBash() {
|
||||
return myRunViaBash;
|
||||
}
|
||||
|
||||
public void loadState(HgGlobalSettings state) {
|
||||
hgExecutable = state.hgExecutable;
|
||||
public void setRunViaBash(boolean runViaBash) {
|
||||
myRunViaBash = runViaBash;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,9 +22,14 @@ import com.intellij.openapi.components.Storage;
|
||||
)
|
||||
public class HgProjectSettings implements PersistentStateComponent<HgProjectSettings.State> {
|
||||
|
||||
private final HgGlobalSettings myAppSettings;
|
||||
private boolean myCheckIncoming = true;
|
||||
private boolean myCheckOutgoing = true;
|
||||
|
||||
public HgProjectSettings(HgGlobalSettings appSettings) {
|
||||
myAppSettings = appSettings;
|
||||
}
|
||||
|
||||
public static class State {
|
||||
public boolean myCheckIncoming = true;
|
||||
public boolean myCheckOutgoing = true;
|
||||
@@ -59,19 +64,27 @@ public class HgProjectSettings implements PersistentStateComponent<HgProjectSett
|
||||
}
|
||||
|
||||
public String getHgExecutable() {
|
||||
return HgGlobalSettings.getInstance().getHgExecutable();
|
||||
return myAppSettings.getHgExecutable();
|
||||
}
|
||||
|
||||
public boolean isAutodetectHg() {
|
||||
return HgGlobalSettings.getInstance().isAutodetectHg();
|
||||
return myAppSettings.isAutodetectHg();
|
||||
}
|
||||
|
||||
public void enableAutodetectHg() {
|
||||
HgGlobalSettings.getInstance().enableAutodetectHg();
|
||||
myAppSettings.enableAutodetectHg();
|
||||
}
|
||||
|
||||
public void setHgExecutable(String text) {
|
||||
HgGlobalSettings.getInstance().setHgExecutable(text);
|
||||
myAppSettings.setHgExecutable(text);
|
||||
}
|
||||
|
||||
public boolean isRunViaBash() {
|
||||
return myAppSettings.isRunViaBash();
|
||||
}
|
||||
|
||||
public void setRunViaBash(boolean runViaBash) {
|
||||
myAppSettings.setRunViaBash(runViaBash);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ public final class HgCommandService {
|
||||
if (arguments != null && arguments.size() != 0) {
|
||||
cmdLine.addAll(arguments);
|
||||
}
|
||||
ShellCommand shellCommand = new ShellCommand();
|
||||
ShellCommand shellCommand = new ShellCommand(mySettings.isRunViaBash());
|
||||
HgCommandResult result;
|
||||
try {
|
||||
String workingDir = repo != null ? repo.getPath() : null;
|
||||
@@ -154,7 +154,7 @@ public final class HgCommandService {
|
||||
result.setWarnings(warnings);
|
||||
|
||||
// logging to the Version Control console (without extensions and configs)
|
||||
final String cmdString = String.format("%s %s %s", HgVcs.HG_EXECUTABLE_FILE_NAME, operation,
|
||||
final String cmdString = String.format("%s %s %s", mySettings.isRunViaBash() ? "bash -c " + HgVcs.HG_EXECUTABLE_FILE_NAME : HgVcs.HG_EXECUTABLE_FILE_NAME, operation,
|
||||
StringUtils.join(arguments, " "));
|
||||
myVcs.showMessageInConsole(cmdString, ConsoleViewContentType.USER_INPUT.getAttributes());
|
||||
if (!silent) {
|
||||
|
||||
@@ -20,12 +20,12 @@ import java.util.Arrays;
|
||||
|
||||
public class HgVersionCommand {
|
||||
|
||||
public boolean isValid(String executable) {
|
||||
public boolean isValid(String executable, boolean isRunViaBash) {
|
||||
String hgExecutable = StringUtils.trim(executable);
|
||||
if (!hgExecutable.endsWith(HgVcs.HG_EXECUTABLE_FILE_NAME)) {
|
||||
return false;
|
||||
}
|
||||
ShellCommand shellCommand = new ShellCommand();
|
||||
ShellCommand shellCommand = new ShellCommand(isRunViaBash);
|
||||
try {
|
||||
return !shellCommand
|
||||
.execute(Arrays.asList(hgExecutable, "version"), null, Charset.defaultCharset())
|
||||
|
||||
@@ -13,9 +13,11 @@
|
||||
package org.zmlx.hg4idea.command;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@@ -24,11 +26,28 @@ final class ShellCommand {
|
||||
private static final Logger LOG = Logger.getInstance(ShellCommand.class.getName());
|
||||
|
||||
private static final int BUFFER_SIZE = 1024;
|
||||
private final boolean myRunViaBash;
|
||||
|
||||
public ShellCommand(boolean runViaBash) {
|
||||
myRunViaBash = runViaBash;
|
||||
}
|
||||
|
||||
public HgCommandResult execute(List<String> commandLine, String dir, Charset charset) throws ShellCommandException, InterruptedException {
|
||||
if (commandLine == null || commandLine.isEmpty()) {
|
||||
throw new IllegalArgumentException("commandLine is empty");
|
||||
}
|
||||
|
||||
if (myRunViaBash) {
|
||||
// run via bash -cl <hg command> => need to escape bash special symbols
|
||||
// '-l' makes bash execute as a login shell thus reading .bash_profile
|
||||
String hgCommand = StringUtil.join(commandLine, " ");
|
||||
hgCommand = StringUtil.escapeStringCharacters(hgCommand.length(), hgCommand, "\b\t\n\f\r|>$\"'&", false, false, new StringBuilder()).toString(); // we don't escape backslash, because we want special unicode characters (\u0017 for hg log)
|
||||
commandLine = new ArrayList<String>(3);
|
||||
commandLine.add("bash");
|
||||
commandLine.add("-cl");
|
||||
commandLine.add(hgCommand);
|
||||
}
|
||||
|
||||
StringWriter out = new StringWriter();
|
||||
StringWriter err = new StringWriter();
|
||||
try {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.zmlx.hg4idea.ui.HgConfigurationProjectPanel">
|
||||
<grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<xy x="20" y="20" width="500" height="400"/>
|
||||
@@ -80,9 +80,17 @@
|
||||
</grid>
|
||||
<vspacer id="4088f">
|
||||
<constraints>
|
||||
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</vspacer>
|
||||
<component id="71ef3" class="javax.swing.JCheckBox" binding="myRunHgAsBashCheckBox" default-binding="true">
|
||||
<constraints>
|
||||
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="Run hg as 'bash -c <path to hg>'"/>
|
||||
</properties>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
<buttonGroups>
|
||||
|
||||
@@ -34,6 +34,7 @@ public class HgConfigurationProjectPanel {
|
||||
private JRadioButton myAutoRadioButton;
|
||||
private JRadioButton mySelectRadioButton;
|
||||
private TextFieldWithBrowseButton myPathSelector;
|
||||
private JCheckBox myRunHgAsBashCheckBox;
|
||||
|
||||
public HgConfigurationProjectPanel(HgProjectSettings projectSettings) {
|
||||
myProjectSettings = projectSettings;
|
||||
@@ -53,12 +54,14 @@ public class HgConfigurationProjectPanel {
|
||||
? !myPathSelector.getText().equals(myProjectSettings.getHgExecutable())
|
||||
: myAutoRadioButton.isSelected() != myProjectSettings.isAutodetectHg();
|
||||
return executableModified || myCheckIncomingCbx.isSelected() != myProjectSettings.isCheckIncoming()
|
||||
|| myCheckOutgoingCbx.isSelected() != myProjectSettings.isCheckOutgoing();
|
||||
|| myCheckOutgoingCbx.isSelected() != myProjectSettings.isCheckOutgoing()
|
||||
|| myRunHgAsBashCheckBox.isSelected() != myProjectSettings.isRunViaBash();
|
||||
}
|
||||
|
||||
public void saveSettings() {
|
||||
myProjectSettings.setCheckIncoming(myCheckIncomingCbx.isSelected());
|
||||
myProjectSettings.setCheckOutgoing(myCheckOutgoingCbx.isSelected());
|
||||
myProjectSettings.setRunViaBash(myRunHgAsBashCheckBox.isSelected());
|
||||
|
||||
if (myAutoRadioButton.isSelected()) {
|
||||
myProjectSettings.enableAutodetectHg();
|
||||
@@ -70,6 +73,7 @@ public class HgConfigurationProjectPanel {
|
||||
public void loadSettings() {
|
||||
myCheckIncomingCbx.setSelected(myProjectSettings.isCheckIncoming());
|
||||
myCheckOutgoingCbx.setSelected(myProjectSettings.isCheckOutgoing());
|
||||
myRunHgAsBashCheckBox.setSelected(myProjectSettings.isRunViaBash());
|
||||
|
||||
boolean isAutodetectHg = myProjectSettings.isAutodetectHg();
|
||||
myAutoRadioButton.setSelected(isAutodetectHg);
|
||||
@@ -94,7 +98,7 @@ public class HgConfigurationProjectPanel {
|
||||
hgExecutable = myPathSelector.getText();
|
||||
}
|
||||
HgVersionCommand command = new HgVersionCommand();
|
||||
if (!command.isValid(hgExecutable)) {
|
||||
if (!command.isValid(hgExecutable, myRunHgAsBashCheckBox.isSelected())) {
|
||||
throw new ConfigurationException(
|
||||
HgVcsMessages.message("hg4idea.configuration.executable.error", hgExecutable)
|
||||
);
|
||||
@@ -102,7 +106,7 @@ public class HgConfigurationProjectPanel {
|
||||
}
|
||||
|
||||
private void createUIComponents() {
|
||||
myPathSelector = new HgSetExecutablePathPanel();
|
||||
myPathSelector = new HgSetExecutablePathPanel(myProjectSettings);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.intellij.openapi.fileChooser.FileChooserDescriptor;
|
||||
import com.intellij.openapi.options.ConfigurationException;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.zmlx.hg4idea.HgProjectSettings;
|
||||
import org.zmlx.hg4idea.HgVcsMessages;
|
||||
import org.zmlx.hg4idea.command.HgVersionCommand;
|
||||
|
||||
@@ -19,12 +20,12 @@ class HgSetExecutablePathPanel extends TextFieldWithBrowseButton {
|
||||
|
||||
private final Set<ActionListener> myOkListeners = new HashSet<ActionListener>();
|
||||
|
||||
HgSetExecutablePathPanel() {
|
||||
HgSetExecutablePathPanel(final HgProjectSettings projectSettings) {
|
||||
FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, false, false, false, false) {
|
||||
public void validateSelectedFiles(VirtualFile[] files) throws Exception {
|
||||
HgVersionCommand command = new HgVersionCommand();
|
||||
String path = files[0].getPath();
|
||||
if (!command.isValid(path)) {
|
||||
if (!command.isValid(path, projectSettings.isRunViaBash())) {
|
||||
throw new ConfigurationException(HgVcsMessages.message("hg4idea.configuration.executable.error", path));
|
||||
}
|
||||
for (ActionListener okListener : myOkListeners) {
|
||||
|
||||
Reference in New Issue
Block a user