From 4f725efed9cd65991de607fd2a2f2535ffdb45d4 Mon Sep 17 00:00:00 2001 From: "Nadya.Zabrodina" Date: Mon, 15 Oct 2012 13:14:51 +0400 Subject: [PATCH] IDEA-64887 Mercurial: change dialog that appears on deleted-modified conflict --- .../zmlx/hg4idea/command/HgUpdateCommand.java | 3 +- .../hg4idea/execution/HgCommandExecutor.java | 93 ++++++++----------- .../HgDeleteModifyPromptHandler.java | 90 ++++++++++++++++++ .../hg4idea/execution/HgPromptChoice.java | 61 ++++++++++++ .../hg4idea/execution/HgPromptHandler.java | 47 ++++++++++ 5 files changed, 240 insertions(+), 54 deletions(-) create mode 100644 plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgDeleteModifyPromptHandler.java create mode 100644 plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgPromptChoice.java create mode 100644 plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgPromptHandler.java diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgUpdateCommand.java b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgUpdateCommand.java index 6194efa48996..f389393ac909 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgUpdateCommand.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgUpdateCommand.java @@ -20,6 +20,7 @@ import org.jetbrains.annotations.Nullable; import org.zmlx.hg4idea.HgVcs; import org.zmlx.hg4idea.execution.HgCommandExecutor; import org.zmlx.hg4idea.execution.HgCommandResult; +import org.zmlx.hg4idea.execution.HgDeleteModifyPromptHandler; import java.util.LinkedList; import java.util.List; @@ -66,7 +67,7 @@ public class HgUpdateCommand { final HgCommandExecutor executor = new HgCommandExecutor(project); executor.setShowOutput(true); - final HgCommandResult result = executor.executeInCurrentThread(repo, "update", arguments); + final HgCommandResult result = executor.executeInCurrentThread(repo, "update", arguments,new HgDeleteModifyPromptHandler()); project.getMessageBus().syncPublisher(HgVcs.BRANCH_TOPIC).update(project); return result; } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgCommandExecutor.java b/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgCommandExecutor.java index 5204f2ded5cb..575cb123fcb8 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgCommandExecutor.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgCommandExecutor.java @@ -13,9 +13,9 @@ package org.zmlx.hg4idea.execution; import com.intellij.execution.ui.ConsoleViewContentType; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.vcsUtil.VcsImplUtil; @@ -28,7 +28,6 @@ import org.zmlx.hg4idea.util.HgEncodingUtil; import org.zmlx.hg4idea.util.HgErrorUtil; import org.zmlx.hg4idea.util.HgUtil; -import javax.swing.*; import java.awt.*; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -52,7 +51,7 @@ import java.util.List; *

*/ public final class HgCommandExecutor { - + private static final Logger LOG = Logger.getInstance(HgCommandExecutor.class.getName()); private static final List DEFAULT_OPTIONS = Arrays.asList("--config", "ui.merge=internal:merge"); @@ -100,6 +99,12 @@ public final class HgCommandExecutor { @Nullable public HgCommandResult executeInCurrentThread(@Nullable final VirtualFile repo, @NotNull final String operation, @Nullable final List arguments) { + return executeInCurrentThread(repo, operation, arguments, null); + } + + @Nullable + public HgCommandResult executeInCurrentThread(@Nullable final VirtualFile repo, @NotNull final String operation, + @Nullable final List arguments, @Nullable HgPromptHandler handler) { //LOG.assertTrue(!ApplicationManager.getApplication().isDispatchThread()); disabled for release if (myProject == null || myProject.isDisposed() || myVcs == null) { return null; @@ -114,11 +119,11 @@ public final class HgCommandExecutor { WarningReceiver warningReceiver = new WarningReceiver(); PassReceiver passReceiver = new PassReceiver(myProject); - - SocketServer promptServer = new SocketServer(new PromptReceiver()); + + SocketServer promptServer = new SocketServer(new PromptReceiver(handler)); SocketServer warningServer = new SocketServer(warningReceiver); SocketServer passServer = new SocketServer(passReceiver); - + try { int promptPort = promptServer.start(); int warningPort = warningServer.start(); @@ -258,37 +263,46 @@ public final class HgCommandExecutor { } private static class PromptReceiver extends SocketServer.Protocol { + @Nullable HgPromptHandler myHandler; + + public PromptReceiver(@Nullable HgPromptHandler handler) { + myHandler = handler; + } public boolean handleConnection(Socket socket) throws IOException { DataInputStream dataInput = new DataInputStream(socket.getInputStream()); DataOutputStream out = new DataOutputStream(socket.getOutputStream()); final String message = new String(readDataBlock(dataInput)); int numOfChoices = dataInput.readInt(); - final Choice[] choices = new Choice[numOfChoices]; + final HgPromptChoice[] choices = new HgPromptChoice[numOfChoices]; for (int i = 0; i < numOfChoices; i++) { String choice = new String(readDataBlock(dataInput)); - choices[i] = new Choice(choice); + choices[i] = new HgPromptChoice(i, choice); } int defaultChoiceInt = dataInput.readInt(); - final Choice defaultChoice = choices[defaultChoiceInt]; - + final HgPromptChoice defaultChoice = choices[defaultChoiceInt]; + if (myHandler != null && myHandler.shouldHandle(message)) { + int chosen = myHandler.promptUser(message, choices, defaultChoice).getChosenIndex(); + sendChoiceToHg(out, chosen); + return true; + } final int[] index = new int[]{-1}; try { EventQueue.invokeAndWait(new Runnable() { public void run() { - Window parent = ApplicationManager.getApplication().getComponent(Window.class); - index[0] = JOptionPane - .showOptionDialog(parent, message, "hg4idea", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, choices, - defaultChoice); + String[] choicePresentationArray = new String[choices.length]; + for (int i = 0; i < choices.length; ++i) { + choicePresentationArray[i] = choices[i].toString(); + } + index[0] = Messages + .showChooseDialog(message, "hg4idea", + choicePresentationArray, + defaultChoice.toString(), Messages.getQuestionIcon()); } }); - + int chosen = index[0]; - if (chosen == JOptionPane.CLOSED_OPTION) { - out.writeInt(-1); - } else { - out.writeInt(chosen); - } + sendChoiceToHg(out, chosen); return true; } catch (InterruptedException e) { @@ -301,43 +315,16 @@ public final class HgCommandExecutor { } } - private static class Choice{ - private final String fullString; - private final String representation; - private final String choiceChar; - - private Choice(String fullString) { - this.fullString = fullString; - this.representation = fullString.replaceAll("&", ""); - int index = fullString.indexOf("&"); - this.choiceChar = "" + fullString.charAt(index + 1); - + private static void sendChoiceToHg(@NotNull DataOutputStream outStream, int choice) throws IOException { + if (choice == HgPromptChoice.CLOSED_OPTION) { + outStream.writeInt(-1); } - - @Override - public String toString() { - return representation; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Choice choice = (Choice) o; - - if (!fullString.equals(choice.fullString)) return false; - - return true; - } - - @Override - public int hashCode() { - return fullString.hashCode(); + else { + outStream.writeInt(choice); } } } - + private static class PassReceiver extends SocketServer.Protocol{ private final Project myProject; private HgCommandAuthenticator myAuthenticator; diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgDeleteModifyPromptHandler.java b/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgDeleteModifyPromptHandler.java new file mode 100644 index 000000000000..ce545ca8f69a --- /dev/null +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgDeleteModifyPromptHandler.java @@ -0,0 +1,90 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.zmlx.hg4idea.execution; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.ui.Messages; +import org.jetbrains.annotations.NotNull; + +import java.awt.*; +import java.lang.reflect.InvocationTargetException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Nadya Zabrodina + */ +public class HgDeleteModifyPromptHandler implements HgPromptHandler { + + private static final Logger LOG = Logger.getInstance("#org.zmlx.hg4idea.execution.HgDeleteModifyPromptHandler"); + + private static final Pattern LOCAL_DELETE_REMOTE_MODIFIED_CONFLICT_MESSAGE_PATTERN = Pattern.compile( + "remote\\schanged(.+)which\\slocal\\sdeleted\\s.+"); + private static final Pattern REMOTE_DELETE_LOCAL_MODIFIED_CONFLICT_MESSAGE_PATTERN = Pattern.compile( + "\\slocal\\schanged(.+)which\\sremote\\sdeleted\\s.+"); + + private String myMessage = ""; + + public HgPromptChoice promptUser(final String message, + @NotNull final HgPromptChoice[] choices, + @NotNull final HgPromptChoice defaultChoice) { + + final int[] chosen = new int[]{-1}; + try { + EventQueue.invokeAndWait + (new Runnable() { + public void run() { + String[] choicePresentationArray = new String[choices.length]; + for (int i = 0; i < choices.length; ++i) { + choicePresentationArray[i] = choices[i].toString(); + } + chosen[0] = Messages + .showChooseDialog(myMessage, "Delete-Modify Conflict", + choicePresentationArray, + defaultChoice.toString(), Messages.getQuestionIcon()); + } + }); + } + catch (InterruptedException e) { + LOG.error(e); + return defaultChoice; + } + catch (InvocationTargetException e) { + LOG.error(e); + return defaultChoice; + } + return chosen[0] >= 0 ? choices[chosen[0]] : HgPromptChoice.ABORT; + } + + public boolean shouldHandle(String message) { + Matcher localDelMatcher = LOCAL_DELETE_REMOTE_MODIFIED_CONFLICT_MESSAGE_PATTERN.matcher(message); + Matcher locaModifMatcher = REMOTE_DELETE_LOCAL_MODIFIED_CONFLICT_MESSAGE_PATTERN.matcher(message); + String filename; + if (localDelMatcher.matches()) { + filename = localDelMatcher.group(1); + myMessage = + "File " + filename + " is deleted locally, but modified remotely. Do you want to keep the modified version or remove the file?"; + return true; + } + else if (locaModifMatcher.matches()) { + filename = locaModifMatcher.group(1); + myMessage = + "File " + filename + " is deleted remotely, but modified locally. Do you want to keep the modified version or remove the file?"; + return true; + } + return false; + } +} diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgPromptChoice.java b/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgPromptChoice.java new file mode 100644 index 000000000000..2880bc983dba --- /dev/null +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgPromptChoice.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.zmlx.hg4idea.execution; + + +public class HgPromptChoice { + private final String fullString; + private final String representation; + private final String choiceChar; + private final int chosenIndex; + public static final int CLOSED_OPTION = -1; + public static final HgPromptChoice ABORT = new HgPromptChoice(-1, "AbortChoice"); + + public int getChosenIndex() { + return chosenIndex; + } + + + HgPromptChoice(int chosenIndex, String fullString) { + this.fullString = fullString; + this.representation = fullString.replaceAll("&", ""); + int index = fullString.indexOf("&"); + this.choiceChar = String.valueOf(fullString.charAt(index + 1)); + this.chosenIndex = chosenIndex; + } + + @Override + public String toString() { + return representation; + } + + @Override + public boolean equals(Object o) { + if (null == o) return true; + if (getClass() != o.getClass()) return false; + + HgPromptChoice choice = (HgPromptChoice)o; + + if (!fullString.equals(choice.fullString)) return false; + + return true; + } + + @Override + public int hashCode() { + return fullString.hashCode(); + } +} \ No newline at end of file diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgPromptHandler.java b/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgPromptHandler.java new file mode 100644 index 000000000000..66e75abca9b6 --- /dev/null +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgPromptHandler.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.zmlx.hg4idea.execution; + +import org.jetbrains.annotations.NotNull; + +/** + * HgPromptHandler is used by {@link HgCommandExecutor}, when you want to change the behavior of standart commands execution in the Mercurial. + * + * @author Nadya Zabrodina + */ +public interface HgPromptHandler { + + /** + * Checks you need to change the default behavior. + * + * @param message standart output message from Mercurial + * @return + */ + boolean shouldHandle(String message); + + /** + * Change default behavior in commands execution. + * + * + * @param message standard output message from Mercurial + * @param choices possible choices + * @param defaultChoice + * @return + */ + HgPromptChoice promptUser(final String message, + @NotNull final HgPromptChoice[] choices, + @NotNull final HgPromptChoice defaultChoice); +}