diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java b/java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java index eeab8ea28473..dbf1ae9d7258 100644 --- a/java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java +++ b/java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java @@ -29,6 +29,8 @@ import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleType; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; @@ -42,6 +44,7 @@ import com.intellij.util.messages.MessageBusConnection; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.lang.reflect.Array; import java.util.*; import java.util.concurrent.Semaphore; @@ -60,6 +63,7 @@ public class CompilerManagerImpl extends CompilerManager { private final Map> myCompilerToInputTypes = new HashMap>(); private final Map> myCompilerToOutputTypes = new HashMap>(); private final Set myValidationDisabledModuleTypes = new HashSet(); + private final Set myWatchRoots; public CompilerManagerImpl(final Project project, CompilerConfigurationImpl compilerConfiguration, MessageBus messageBus) { myProject = project; @@ -82,6 +86,17 @@ public class CompilerManagerImpl extends CompilerManager { } addCompilableFileType(StdFileTypes.JAVA); + + final File projectGeneratedSrcRoot = CompilerPaths.getGeneratedDataDirectory(project); + FileUtil.createIfDoesntExist(projectGeneratedSrcRoot); + final LocalFileSystem lfs = LocalFileSystem.getInstance(); + myWatchRoots = lfs.addRootsToWatch(Collections.singletonList(FileUtil.toCanonicalPath(projectGeneratedSrcRoot.getPath())), true); + Disposer.register(project, new Disposable() { + public void dispose() { + lfs.removeWatchedRoots(myWatchRoots); + } + }); + // //addCompiler(new DummyTransformingCompiler()); // this one is for testing purposes only //addCompiler(new DummySourceGeneratingCompiler(myProject)); // this one is for testing purposes only diff --git a/java/java-impl/src/com/intellij/refactoring/util/duplicates/DuplicatesImpl.java b/java/java-impl/src/com/intellij/refactoring/util/duplicates/DuplicatesImpl.java index f1999e081c2f..052c51891c0b 100644 --- a/java/java-impl/src/com/intellij/refactoring/util/duplicates/DuplicatesImpl.java +++ b/java/java-impl/src/com/intellij/refactoring/util/duplicates/DuplicatesImpl.java @@ -21,6 +21,8 @@ import com.intellij.codeInsight.highlighting.HighlightManager; import com.intellij.find.FindManager; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationNamesInfo; +import com.intellij.openapi.application.Result; +import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.FoldRegion; @@ -127,8 +129,10 @@ public class DuplicatesImpl { } } HighlightManager.getInstance(project).removeSegmentHighlighter(editor, highlighters.get(0)); - final Runnable action = new Runnable() { - public void run() { + + new WriteCommandAction(project, MethodDuplicatesHandler.REFACTORING_NAME) { + @Override + protected void run(Result result) throws Throwable { try { provider.processMatch(match); } @@ -136,10 +140,7 @@ public class DuplicatesImpl { LOG.error(e); } } - }; - - //use outer command - ApplicationManager.getApplication().runWriteAction(action); + }.execute(); return false; } diff --git a/java/java-tests/testData/refactoring/changeSignatureGesture/OnAnotherMethod.java b/java/java-tests/testData/refactoring/changeSignatureGesture/OnAnotherMethod.java new file mode 100644 index 000000000000..b863a3650ee6 --- /dev/null +++ b/java/java-tests/testData/refactoring/changeSignatureGesture/OnAnotherMethod.java @@ -0,0 +1,5 @@ +class Test { + void foo() { + } + void bar(){foo();} +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/renameInplace/SuperMethodAnonymousInheritor.java b/java/java-tests/testData/refactoring/renameInplace/SuperMethodAnonymousInheritor.java new file mode 100644 index 000000000000..d30f2dacbd38 --- /dev/null +++ b/java/java-tests/testData/refactoring/renameInplace/SuperMethodAnonymousInheritor.java @@ -0,0 +1,16 @@ +class Demo { + class MyEvent {} + interface MyEventListener { + void action(MyEvent event); + } + + class Driver { + void method() { + MyEventListener l = new MyEventListener() { + public void action(MyEvent event) { + //To change body of implemented methods use File | Settings | File Templates. + } + }; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/renameInplace/SuperMethodAnonymousInheritor_after.java b/java/java-tests/testData/refactoring/renameInplace/SuperMethodAnonymousInheritor_after.java new file mode 100644 index 000000000000..ee9f9c67aea5 --- /dev/null +++ b/java/java-tests/testData/refactoring/renameInplace/SuperMethodAnonymousInheritor_after.java @@ -0,0 +1,16 @@ +class Demo { + class MyEvent {} + interface MyEventListener { + void xxx(MyEvent event); + } + + class Driver { + void method() { + MyEventListener l = new MyEventListener() { + public void xxx(MyEvent event) { + //To change body of implemented methods use File | Settings | File Templates. + } + }; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/refactoring/ChangeSignatureGestureTest.java b/java/java-tests/testSrc/com/intellij/refactoring/ChangeSignatureGestureTest.java index 219e117e1d8e..79db612ff419 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/ChangeSignatureGestureTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/ChangeSignatureGestureTest.java @@ -25,10 +25,7 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.actions.EditorActionUtil; import com.intellij.openapi.editor.ex.EditorEx; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiManager; -import com.intellij.psi.PsiMethod; -import com.intellij.psi.PsiTypeElement; +import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.changeSignature.ChangeSignatureDetectorAction; import com.intellij.refactoring.changeSignature.ChangeSignatureGestureDetector; @@ -100,6 +97,17 @@ public class ChangeSignatureGestureTest extends LightCodeInsightFixtureTestCase doTypingNoBorderTest("int param"); } + public void testOnAnotherMethod() { + doTest(new Runnable() { + @Override + public void run() { + myFixture.type("int param"); + final int nextMethodOffset = ((PsiJavaFile)myFixture.getFile()).getClasses()[0].getMethods()[1].getTextOffset(); + myFixture.getEditor().getCaretModel().moveToOffset(nextMethodOffset); + } + }, false, ChangeSignatureDetectorAction.CHANGE_SIGNATURE); + } + public void testAddParamChangeReturnType() { doTest(new Runnable() { @Override diff --git a/java/java-tests/testSrc/com/intellij/refactoring/RenameMembersInplaceTest.java b/java/java-tests/testSrc/com/intellij/refactoring/RenameMembersInplaceTest.java index d551d0ea8580..86729522a0d6 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/RenameMembersInplaceTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/RenameMembersInplaceTest.java @@ -45,6 +45,10 @@ public class RenameMembersInplaceTest extends LightCodeInsightTestCase { public void testSuperMethod() throws Exception { doTestInplaceRename("xxx"); } + + public void testSuperMethodAnonymousInheritor() throws Exception { + doTestInplaceRename("xxx"); + } public void testMultipleConstructors() throws Exception { doTestInplaceRename("Bar"); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/enter/BaseIndentEnterHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/enter/BaseIndentEnterHandler.java index 285eaa828930..bb059ff0e01d 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/enter/BaseIndentEnterHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/enter/BaseIndentEnterHandler.java @@ -126,8 +126,8 @@ public class BaseIndentEnterHandler extends EnterHandlerDelegateAdapter { } else { if (myIndentTokens.contains(type)) { - final String singleIndent = getSingleIndent(file, lineIndent); - EditorModificationUtil.insertStringAtCaret(editor, "\n" + lineIndent + singleIndent); + final String newIndent = getNewIndent(file, lineIndent); + EditorModificationUtil.insertStringAtCaret(editor, "\n" + newIndent); return Result.Stop; } @@ -137,6 +137,15 @@ public class BaseIndentEnterHandler extends EnterHandlerDelegateAdapter { } } + protected String getNewIndent(final @NotNull PsiFile file, final @NotNull CharSequence oldIndent) { + if (oldIndent.length() > 0 && oldIndent.charAt(oldIndent.length() - 1) == '\t') { + return oldIndent + "\t"; + } + final CodeStyleSettings currentSettings = CodeStyleSettingsManager.getSettings(file.getProject()); + final CommonCodeStyleSettings.IndentOptions indentOptions = currentSettings.getIndentOptions(file.getFileType()); + return oldIndent + StringUtil.repeatSymbol(' ', indentOptions.INDENT_SIZE); + } + private static int calcLogicalLength(Editor editor, CharSequence lineIndent) { int result = 0; for (int i = 0; i < lineIndent.length(); i++) { @@ -149,15 +158,6 @@ public class BaseIndentEnterHandler extends EnterHandlerDelegateAdapter { return result; } - protected static String getSingleIndent(final PsiFile file, CharSequence lineIndent) { - if (lineIndent.length() > 0 && lineIndent.charAt(lineIndent.length() - 1) == '\t') { - return "\t"; - } - CodeStyleSettings currentSettings = CodeStyleSettingsManager.getSettings(file.getProject()); - CommonCodeStyleSettings.IndentOptions indentOptions = currentSettings.getIndentOptions(file.getFileType()); - return StringUtil.repeatSymbol(' ', indentOptions.INDENT_SIZE); - } - @Nullable private IElementType getNonWhitespaceElementType(final HighlighterIterator iterator, final int lineStartOffset) { while (!iterator.atEnd() && iterator.getStart() >= lineStartOffset) { diff --git a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleViewImpl.java b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleViewImpl.java index 7dfcb5d5468e..336f6cb9b449 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleViewImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleViewImpl.java @@ -61,10 +61,7 @@ public class LanguageConsoleViewImpl extends ConsoleViewImpl { return myConsole.getComponent(); } - public JComponent getComponent() { - return super.getComponent(); - } - + @Override public JComponent getPreferredFocusableComponent() { return myConsole.getConsoleEditor().getContentComponent(); } diff --git a/platform/lang-impl/src/com/intellij/execution/filters/TextConsoleBuilderImpl.java b/platform/lang-impl/src/com/intellij/execution/filters/TextConsoleBuilderImpl.java index 773e5201278a..966dcd040a59 100644 --- a/platform/lang-impl/src/com/intellij/execution/filters/TextConsoleBuilderImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/filters/TextConsoleBuilderImpl.java @@ -41,6 +41,7 @@ public class TextConsoleBuilderImpl extends TextConsoleBuilder { myScope = scope; } + @Override public ConsoleView getConsole() { final ConsoleView consoleView = createConsole(); for (final Filter filter : myFilters) { @@ -53,6 +54,7 @@ public class TextConsoleBuilderImpl extends TextConsoleBuilder { return new ConsoleViewImpl(myProject, myScope, myViewer, null); } + @Override public void addFilter(final Filter filter) { myFilters.add(filter); } diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ConfigurationSettingsEditorWrapper.java b/platform/lang-impl/src/com/intellij/execution/impl/ConfigurationSettingsEditorWrapper.java index 61835db7f5b3..61602caf11fc 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ConfigurationSettingsEditorWrapper.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ConfigurationSettingsEditorWrapper.java @@ -28,7 +28,6 @@ import com.intellij.openapi.actionSystem.TypeSafeDataProvider; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.SettingsEditor; import com.intellij.openapi.util.Disposer; -import net.miginfocom.swing.MigLayout; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -60,8 +59,7 @@ public class ConfigurationSettingsEditorWrapper extends SettingsEditor messages) { myMessages = messages; } diff --git a/platform/vcs-api/src/com/intellij/util/continuation/ContinuationContext.java b/platform/vcs-api/src/com/intellij/util/continuation/ContinuationContext.java index 02c3733fd0af..571189b5cd56 100644 --- a/platform/vcs-api/src/com/intellij/util/continuation/ContinuationContext.java +++ b/platform/vcs-api/src/com/intellij/util/continuation/ContinuationContext.java @@ -37,7 +37,7 @@ public interface ContinuationContext extends ContinuationPause { void cancelEverything(); void addExceptionHandler(final Class clazz, final Consumer consumer); - boolean handleException(final Exception e); + boolean handleException(final Exception e, boolean cancelEveryThing); void keepExisting(final Object disaster, final Object cure); void throwDisaster(final Object disaster, final Object cure); diff --git a/platform/vcs-api/src/com/intellij/util/continuation/GatheringContinuationContext.java b/platform/vcs-api/src/com/intellij/util/continuation/GatheringContinuationContext.java index dbfb12891517..ea116c80b610 100644 --- a/platform/vcs-api/src/com/intellij/util/continuation/GatheringContinuationContext.java +++ b/platform/vcs-api/src/com/intellij/util/continuation/GatheringContinuationContext.java @@ -47,7 +47,7 @@ public class GatheringContinuationContext implements ContinuationContext { } @Override - public boolean handleException(Exception e) { + public boolean handleException(Exception e, boolean cancelEveryThing) { return false; } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java index 0278d7fa5958..c40d3c5f41ca 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java @@ -18,10 +18,7 @@ package com.intellij.openapi.vcs.changes.patch; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; -import com.intellij.openapi.diff.impl.patch.PatchReader; -import com.intellij.openapi.diff.impl.patch.PatchSyntaxException; -import com.intellij.openapi.diff.impl.patch.PatchVirtualFileReader; -import com.intellij.openapi.diff.impl.patch.TextFilePatch; +import com.intellij.openapi.diff.impl.patch.*; import com.intellij.openapi.fileChooser.FileChooser; import com.intellij.openapi.fileChooser.FileChooserDescriptor; import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; @@ -33,10 +30,7 @@ import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.PopupStep; import com.intellij.openapi.ui.popup.util.BaseListPopupStep; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.Getter; -import com.intellij.openapi.util.IconLoader; -import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.*; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.ObjectsConvertor; import com.intellij.openapi.vcs.VcsBundle; @@ -95,10 +89,22 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { private JLabel myPatchFileLabel; private PatchReader myReader; private CommitContext myCommitContext; - private final VirtualFileAdapter myListener; + private VirtualFileAdapter myListener; + private boolean myCanChangePatchFile; public ApplyPatchDifferentiatedDialog(final Project project, final ApplyPatchExecutor callback, final List executors, @NotNull final ApplyPatchMode applyPatchMode, @NotNull final VirtualFile patchFile) { + this(project, callback, executors, applyPatchMode, patchFile, null, null); + } + + public ApplyPatchDifferentiatedDialog(final Project project, final ApplyPatchExecutor callback, final List executors, + @NotNull final ApplyPatchMode applyPatchMode, @NotNull final List patches, @Nullable final LocalChangeList defaultList) { + this(project, callback, executors, applyPatchMode, null, patches, defaultList); + } + + private ApplyPatchDifferentiatedDialog(final Project project, final ApplyPatchExecutor callback, final List executors, + @NotNull final ApplyPatchMode applyPatchMode, @Nullable final VirtualFile patchFile, @Nullable final List patches, + @Nullable final LocalChangeList defaultList) { super(project, true); myCallback = callback; myExecutors = executors; @@ -107,18 +113,8 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { final FileChooserDescriptor descriptor = createSelectPatchDescriptor(); descriptor.setTitle(VcsBundle.message("patch.apply.select.title")); - myUpdater = new MyUpdater(); - myPatchFile = new TextFieldWithBrowseButton(); - myPatchFile.addBrowseFolderListener(VcsBundle.message("patch.apply.select.title"), "", project, descriptor); - myPatchFile.getTextField().getDocument().addDocumentListener(new DocumentAdapter() { - protected void textChanged(DocumentEvent e) { - setPathFileChangeDefault(); - myLoadQueue.queue(myUpdater); - } - }); myProject = project; - myLoadQueue = new ZipperUpdater(500, getDisposable()); myPatches = new LinkedList(); myRecentPathFileChange = new AtomicReference(); myChangesTreeList = new MyChangeTreeList(project, Collections.emptyList(), @@ -138,11 +134,24 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { myCommitLegendPanel.update(); } }, new MyChangeNodeDecorator()); - myReset = new Runnable() { + + myUpdater = new MyUpdater(); + myPatchFile = new TextFieldWithBrowseButton(); + myPatchFile.addBrowseFolderListener(VcsBundle.message("patch.apply.select.title"), "", project, descriptor); + myPatchFile.getTextField().getDocument().addDocumentListener(new DocumentAdapter() { + protected void textChanged(DocumentEvent e) { + setPathFileChangeDefault(); + myLoadQueue.queue(myUpdater); + } + }); + + myLoadQueue = new ZipperUpdater(500, getDisposable()); + myCanChangePatchFile = applyPatchMode.isCanChangePatchFile(); + myReset = myCanChangePatchFile ? new Runnable() { public void run() { reset(); } - }; + } : EmptyRunnable.getInstance(); myChangeListChooser = new ChangeListChooserPanel(project, new Consumer() { public void consume(final String errorMessage) { @@ -160,27 +169,48 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { init(); - if (patchFile.isValid()) { + if (patchFile != null && patchFile.isValid()) { init(patchFile); + } else if (patches != null) { + init(patches, defaultList); } - myPatchFileLabel.setVisible(applyPatchMode.isCanChangePatchFile()); - myPatchFile.setVisible(applyPatchMode.isCanChangePatchFile()); - myListener = new VirtualFileAdapter() { - @Override - public void contentsChanged(VirtualFileEvent event) { - if (myRecentPathFileChange.get() != null && myRecentPathFileChange.get().getVf() != null && - myRecentPathFileChange.get().getVf().equals(event.getFile())) { - myLoadQueue.queue(myUpdater); + myPatchFileLabel.setVisible(myCanChangePatchFile); + myPatchFile.setVisible(myCanChangePatchFile); + + if (myCanChangePatchFile) { + myListener = new VirtualFileAdapter() { + @Override + public void contentsChanged(VirtualFileEvent event) { + if (myRecentPathFileChange.get() != null && myRecentPathFileChange.get().getVf() != null && + myRecentPathFileChange.get().getVf().equals(event.getFile())) { + myLoadQueue.queue(myUpdater); + } } - } - }; - final VirtualFileManager fileManager = VirtualFileManager.getInstance(); - fileManager.addVirtualFileListener(myListener); - Disposer.register(getDisposable(), new Disposable() { - @Override - public void dispose() { - fileManager.removeVirtualFileListener(myListener); + }; + final VirtualFileManager fileManager = VirtualFileManager.getInstance(); + fileManager.addVirtualFileListener(myListener); + Disposer.register(getDisposable(), new Disposable() { + @Override + public void dispose() { + fileManager.removeVirtualFileListener(myListener); + } + }); + } + } + + private void init(List patches, final LocalChangeList localChangeList) { + final List matchedPathes = new AutoMatchIterator(myProject).execute(patches); + + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (localChangeList != null) { + myChangeListChooser.setDefaultSelection(localChangeList); + } + + myPatches.clear(); + myPatches.addAll(matchedPathes); + updateTree(true); } }); } @@ -225,7 +255,7 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { patchGroups.putValue(patchInProgress.getBase(), patchInProgress); } final LocalChangeList selected = getSelectedChangeList(); - executor.apply(patchGroups, selected, myRecentPathFileChange.get().getVf().getName(), + executor.apply(patchGroups, selected, myRecentPathFileChange.get() == null ? null : myRecentPathFileChange.get().getVf().getName(), myReader == null ? null : myReader.getAdditionalInfo(ApplyPatchDefaultExecutor.pathsFromGroups(patchGroups))); } @@ -369,12 +399,14 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { group.add(new StripDown()); group.add(new ResetStrip()); group.add(new ZeroStrip()); - group.add(new AnAction("Refresh", "Refresh", IconLoader.getIcon("/actions/sync.png")) { - @Override - public void actionPerformed(AnActionEvent e) { - myLoadQueue.queue(myUpdater); - } - }); + if (myCanChangePatchFile) { + group.add(new AnAction("Refresh", "Refresh", IconLoader.getIcon("/actions/sync.png")) { + @Override + public void actionPerformed(AnActionEvent e) { + myLoadQueue.queue(myUpdater); + } + }); + } final ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar("APPLY_PATCH", group, true); myCenterPanel.add(toolbar.getComponent(), gb); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchMode.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchMode.java index 662031e6e127..3d3363325a64 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchMode.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchMode.java @@ -24,7 +24,8 @@ import com.intellij.openapi.vcs.VcsBundle; */ public enum ApplyPatchMode { APPLY(VcsBundle.message("patch.apply.dialog.title"), true), - UNSHELVE(VcsBundle.message("unshelve.changes.dialog.title"), false); + UNSHELVE(VcsBundle.message("unshelve.changes.dialog.title"), false), + APPLY_PATCH_IN_MEMORY(VcsBundle.message("patch.apply.dialog.title"), false); private final String myTitle; private final boolean myCanChangePatchFile; diff --git a/platform/vcs-impl/src/com/intellij/util/continuation/GeneralRunner.java b/platform/vcs-impl/src/com/intellij/util/continuation/GeneralRunner.java index aa5ae0012067..9e94a0be9b47 100644 --- a/platform/vcs-impl/src/com/intellij/util/continuation/GeneralRunner.java +++ b/platform/vcs-impl/src/com/intellij/util/continuation/GeneralRunner.java @@ -77,19 +77,25 @@ abstract class GeneralRunner implements ContinuationContext { } @Override - public boolean handleException(Exception e) { + public boolean handleException(Exception e, boolean cancelEveryThing) { synchronized (myQueueLock) { - final Class aClass = e.getClass(); - Consumer consumer = myHandlersMap.get(e.getClass()); - if (consumer != null) { - consumer.consume(e); - return true; - } - for (Map.Entry, Consumer> entry : myHandlersMap.entrySet()) { - if (entry.getKey().isAssignableFrom(aClass)) { - entry.getValue().consume(e); + try { + final Class aClass = e.getClass(); + Consumer consumer = myHandlersMap.get(e.getClass()); + if (consumer != null) { + consumer.consume(e); return true; } + for (Map.Entry, Consumer> entry : myHandlersMap.entrySet()) { + if (entry.getKey().isAssignableFrom(aClass)) { + entry.getValue().consume(e); + return true; + } + } + } finally { + if (cancelEveryThing) { + cancelEverything(); + } } } return false; diff --git a/plugins/git4idea/src/git4idea/history/wholeTree/GitCommitsSequentialIndex.java b/plugins/git4idea/src/git4idea/history/wholeTree/GitCommitsSequentialIndex.java index 781db06e0af7..70239c1b1a5d 100644 --- a/plugins/git4idea/src/git4idea/history/wholeTree/GitCommitsSequentialIndex.java +++ b/plugins/git4idea/src/git4idea/history/wholeTree/GitCommitsSequentialIndex.java @@ -361,7 +361,7 @@ public class GitCommitsSequentialIndex implements GitCommitsSequentially { } catch (VcsException e) { context.cancelEverything(); - if (! context.handleException(e)) { + if (! context.handleException(e, false)) { VcsBalloonProblemNotifier.showOverChangesView(myProject, e.getMessage(), MessageType.ERROR); // and exit, do not ping } diff --git a/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java b/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java index 0d551e4d4ee2..48bb90ea6839 100644 --- a/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java +++ b/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java @@ -74,7 +74,7 @@ public class GitStashChangesSaver extends GitChangesSaver { load(); } catch (VcsException e) { - context.handleException(e); + context.handleException(e, false); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcModuleStructureSynchronizer.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcModuleStructureSynchronizer.java index e9d453a06822..04b1c88bb1ab 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcModuleStructureSynchronizer.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcModuleStructureSynchronizer.java @@ -62,6 +62,8 @@ public class MvcModuleStructureSynchronizer extends AbstractProjectComponent { private boolean myOutOfModuleDirectoryCreatedActionAdded; + public static boolean ourGrailsTestFlag; + private final ModificationTracker myModificationTracker = new ModificationTracker() { @Override public long getModificationCount() { @@ -244,17 +246,12 @@ public class MvcModuleStructureSynchronizer extends AbstractProjectComponent { StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new DumbAwareRunnable() { @Override public void run() { - if (ApplicationManager.getApplication().isUnitTestMode()) { - runActions(); - } - else { - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - runActions(); - } - }, ModalityState.NON_MODAL); - } + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + runActions(); + } + }, ModalityState.NON_MODAL); } }); } @@ -295,6 +292,10 @@ public class MvcModuleStructureSynchronizer extends AbstractProjectComponent { return; } + if (ApplicationManager.getApplication().isUnitTestMode() && !ourGrailsTestFlag) { + return; + } + Pair[] actions = myActions.toArray(new Pair[myActions.size()]); //get module by object and kill duplicates diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.java index 1736e8219029..1c92e44d2432 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.java @@ -99,9 +99,7 @@ public class MavenProjectsTree { result.myRootProjects.addAll(readProjectsRecursively(in, result)); } catch (Throwable e) { - IOException ioException = new IOException(); - ioException.initCause(e); - throw ioException; + throw new IOException(e); } } finally { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfiguration.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfiguration.java index 25dbea3c5926..8198915d6eff 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfiguration.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfiguration.java @@ -111,6 +111,7 @@ public class SvnConfiguration implements PersistentStateComponent { public boolean IGNORE_SPACES_IN_ANNOTATE = true; public boolean SHOW_MERGE_SOURCES_IN_ANNOTATE = true; public boolean FORCE_UPDATE = false; + public Boolean TREE_CONFLICT_MERGE_THEIRS_NEW_INTO_OLD_PLACE; public UseAcceleration myUseAcceleration = UseAcceleration.nothing; @@ -409,6 +410,10 @@ public class SvnConfiguration implements PersistentStateComponent { if (cleanupRun != null) { myCleanupRun = Boolean.parseBoolean(cleanupRun.getValue()); } + final Attribute treeConflictMergeNewFilesPlace = element.getAttribute("TREE_CONFLICT_MERGE_THEIRS_NEW_INTO_OLD_PLACE"); + if (treeConflictMergeNewFilesPlace != null) { + TREE_CONFLICT_MERGE_THEIRS_NEW_INTO_OLD_PLACE = Boolean.parseBoolean(treeConflictMergeNewFilesPlace.getValue()); + } } @SuppressWarnings({"HardCodedStringLiteral"}) @@ -444,6 +449,9 @@ public class SvnConfiguration implements PersistentStateComponent { element.setAttribute("myUseAcceleration", "" + myUseAcceleration); element.setAttribute("myAutoUpdateAfterCommit", "" + myAutoUpdateAfterCommit); element.setAttribute(CLEANUP_ON_START_RUN, "" + myCleanupRun); + if (TREE_CONFLICT_MERGE_THEIRS_NEW_INTO_OLD_PLACE != null) { + element.setAttribute("TREE_CONFLICT_MERGE_THEIRS_NEW_INTO_OLD_PLACE", "" + TREE_CONFLICT_MERGE_THEIRS_NEW_INTO_OLD_PLACE); + } } public boolean isAutoUpdateAfterCommit() { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java index 21a3de4a9063..00c62c3f744f 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java @@ -49,6 +49,7 @@ import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings; import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.util.Processor; import com.intellij.util.ThreeState; import com.intellij.util.containers.Convertor; import com.intellij.util.containers.SoftHashMap; @@ -72,6 +73,8 @@ import org.jetbrains.idea.svn.history.SvnHistoryProvider; import org.jetbrains.idea.svn.rollback.SvnRollbackEnvironment; import org.jetbrains.idea.svn.update.SvnIntegrateEnvironment; import org.jetbrains.idea.svn.update.SvnUpdateEnvironment; +import org.tmatesoft.sqljet.core.SqlJetErrorCode; +import org.tmatesoft.sqljet.core.SqlJetException; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager; import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory; @@ -156,6 +159,21 @@ public class SvnVcs extends AbstractVcs { public static final String SVNKIT_HTTP_SSL_PROTOCOLS = "svnkit.http.sslProtocols"; private final SvnExecutableChecker myChecker; + public static final Processor ourBusyExceptionProcessor = new Processor() { + @Override + public boolean process(Exception e) { + if (e instanceof SVNException) { + if (SVNErrorCode.SQLITE_ERROR.equals(((SVNException)e).getErrorMessage().getErrorCode())) { + Throwable cause = ((SVNException)e).getErrorMessage().getCause(); + if (cause instanceof SqlJetException) { + return SqlJetErrorCode.BUSY.equals(((SqlJetException)cause).getErrorCode()); + } + } + } + return false; + } + }; + public void checkCommandLineVersion() { myChecker.checkExecutableAndNotifyIfNeeded(); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/ApplyPatchSaveToFileExecutor.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/ApplyPatchSaveToFileExecutor.java new file mode 100644 index 000000000000..063d4b330093 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/ApplyPatchSaveToFileExecutor.java @@ -0,0 +1,127 @@ +/* + * 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.jetbrains.idea.svn.treeConflict; + +import com.intellij.CommonBundle; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.diff.impl.patch.*; +import com.intellij.openapi.fileChooser.*; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.FilePathImpl; +import com.intellij.openapi.vcs.VcsBundle; +import com.intellij.openapi.vcs.changes.*; +import com.intellij.openapi.vcs.changes.patch.ApplyPatchDefaultExecutor; +import com.intellij.openapi.vcs.changes.patch.ApplyPatchExecutor; +import com.intellij.openapi.vcs.changes.patch.FilePatchInProgress; +import com.intellij.openapi.vcs.changes.patch.PatchWriter; +import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileWrapper; +import com.intellij.util.WaitForProgressToShow; +import com.intellij.util.containers.MultiMap; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 5/17/12 + * Time: 6:02 PM + */ +public class ApplyPatchSaveToFileExecutor implements ApplyPatchExecutor { + private final Project myProject; + private final VirtualFile myBaseForPatch; + private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.treeConflict.ApplyPatchSaveToFileExecutor"); + + public ApplyPatchSaveToFileExecutor(Project project, VirtualFile baseForPatch) { + myProject = project; + myBaseForPatch = baseForPatch; + } + + @Override + public String getName() { + return "Save patch to file"; + } + + @Override + public void apply(MultiMap patchGroups, + LocalChangeList localList, + String fileName, + TransparentlyFailedValue>, PatchSyntaxException> additionalInfo) { + final FileSaverDialog dialog = FileChooserFactory.getInstance().createSaveFileDialog( + new FileSaverDescriptor("Save patch to", ""), myProject); + final VirtualFile baseDir = myProject.getBaseDir(); + final VirtualFileWrapper save = dialog.save(baseDir, "TheirsChanges.patch"); + if (save != null && save.getFile() != null) { + final CommitContext commitContext = new CommitContext(); + + final VirtualFile baseForPatch = myBaseForPatch == null ? baseDir : myBaseForPatch; + try { + final List textPatches = patchGroupsToOneGroup(patchGroups, baseForPatch); + commitContext.putUserData(BaseRevisionTextPatchEP.ourPutBaseRevisionTextKey, false); + PatchWriter.writePatches(myProject, save.getFile().getPath(), textPatches, commitContext, CharsetToolkit.UTF8_CHARSET); + } + catch (final IOException e) { + LOG.info(e); + WaitForProgressToShow.runOrInvokeLaterAboveProgress(new Runnable() { + public void run() { + Messages.showErrorDialog(myProject, VcsBundle.message("create.patch.error.title", e.getMessage()), CommonBundle.getErrorTitle()); + } + }, null, myProject); + } + } + } + + public static List patchGroupsToOneGroup(MultiMap patchGroups, VirtualFile baseDir) + throws IOException { + final List textPatches = new ArrayList(); + final String baseDirPath = baseDir.getPath(); + + for (Map.Entry> entry : patchGroups.entrySet()) { + final VirtualFile vf = entry.getKey(); + final String currBasePath = vf.getPath(); + final String relativePath = VfsUtil.getRelativePath(vf, baseDir, '/'); + final boolean toConvert = !StringUtil.isEmptyOrSpaces(relativePath) && !".".equals(relativePath); + for (FilePatchInProgress patchInProgress : entry.getValue()) { + final TextFilePatch patch = patchInProgress.getPatch(); + if (toConvert) { + //correct paths + patch.setBeforeName(convertRelativePath(patch.getBeforeName(), currBasePath, baseDirPath)); + patch.setAfterName(convertRelativePath(patch.getAfterName(), currBasePath, baseDirPath)); + } + textPatches.add(patch); + } + } + return textPatches; + } + + private static String convertRelativePath(String pathInPatch, String currentBase, String baseDirPath) throws IOException { + if (StringUtil.isEmptyOrSpaces(pathInPatch)) return pathInPatch; + final File currentPath = new File(currentBase, pathInPatch); + return FileUtil.getRelativePath(FileUtil.toSystemIndependentName(baseDirPath), FileUtil.toSystemIndependentName(currentPath.getCanonicalPath()), '/'); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/MergeFromTheirsResolver.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/MergeFromTheirsResolver.java new file mode 100644 index 000000000000..2c52d43b4b60 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/MergeFromTheirsResolver.java @@ -0,0 +1,640 @@ +/* + * 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.jetbrains.idea.svn.treeConflict; + +import com.intellij.CommonBundle; +import com.intellij.openapi.application.Application; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diff.impl.patch.*; +import com.intellij.openapi.diff.impl.patch.formove.PatchApplier; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vcs.*; +import com.intellij.openapi.vcs.changes.*; +import com.intellij.openapi.vcs.changes.committed.CommittedChangesTreeBrowser; +import com.intellij.openapi.vcs.changes.patch.ApplyPatchDifferentiatedDialog; +import com.intellij.openapi.vcs.changes.patch.ApplyPatchExecutor; +import com.intellij.openapi.vcs.changes.patch.ApplyPatchMode; +import com.intellij.openapi.vcs.changes.patch.FilePatchInProgress; +import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier; +import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings; +import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.Consumer; +import com.intellij.util.SmartList; +import com.intellij.util.containers.Convertor; +import com.intellij.util.containers.MultiMap; +import com.intellij.util.continuation.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.*; +import org.jetbrains.idea.svn.history.SvnChangeList; +import org.jetbrains.idea.svn.history.SvnRepositoryLocation; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNNodeKind; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc.SVNTreeConflictDescription; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 5/18/12 + * Time: 2:44 PM + */ +public class MergeFromTheirsResolver { + private final SvnVcs myVcs; + private final SVNTreeConflictDescription myDescription; + private final Change myChange; + private final FilePath myOldFilePath; + private final FilePath myNewFilePath; + private final String myOldPresentation; + private final String myNewPresentation; + private final SvnRevisionNumber myCommittedRevision; + private Boolean myAdd; + + private final List myTheirsChanges; + private final List myTheirsBinaryChanges; + private final List myWarnings; + private List myTextPatches; + private VirtualFile myBaseForPatch; + + public MergeFromTheirsResolver(SvnVcs vcs, SVNTreeConflictDescription description, Change change, SvnRevisionNumber revision) { + myVcs = vcs; + myDescription = description; + myChange = change; + myCommittedRevision = revision; + myOldFilePath = myChange.getBeforeRevision().getFile(); + myNewFilePath = myChange.getAfterRevision().getFile(); + myBaseForPatch = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public VirtualFile compute() { + return ChangesUtil.findValidParent(myNewFilePath); + } + }); + myOldPresentation = TreeConflictRefreshablePanel.filePath(myOldFilePath); + myNewPresentation = TreeConflictRefreshablePanel.filePath(myNewFilePath); + + myTheirsChanges = new ArrayList(); + myTheirsBinaryChanges = new ArrayList(); + myWarnings = new ArrayList(); + myTextPatches = Collections.emptyList(); + } + + public void execute() { + int ok = Messages.showOkCancelDialog(myVcs.getProject(), (myChange.isMoved() ? + SvnBundle.message("confirmation.resolve.tree.conflict.merge.moved", myOldPresentation, myNewPresentation) : + SvnBundle.message("confirmation.resolve.tree.conflict.merge.renamed", myOldPresentation, myNewPresentation)), + TreeConflictRefreshablePanel.TITLE, Messages.getQuestionIcon()); + if (Messages.OK != ok) return; + + FileDocumentManager.getInstance().saveAllDocuments(); + //final String name = "Merge changes from theirs for: " + myOldPresentation; + + final Continuation fragmented = Continuation.createFragmented(myVcs.getProject(), false); + fragmented.addExceptionHandler(VcsException.class, new Consumer() { + @Override + public void consume(VcsException e) { + myWarnings.add(e); + if (e.isWarning()) { + return; + } + AbstractVcsHelper.getInstance(myVcs.getProject()).showErrors(myWarnings, TreeConflictRefreshablePanel.TITLE); + } + }); + + final List tasks = new SmartList(); + if (SVNNodeKind.DIR.equals(myDescription.getNodeKind())) { + tasks.add(new PreloadChangesContentsForDir()); + } else { + tasks.add(new PreloadChangesContentsForFile()); + } + tasks.add(new ConvertTextPaths()); + tasks.add(new PatchCreator()); + tasks.add(new SelectPatchesInApplyPatchDialog()); + tasks.add(new SelectBinaryFiles()); + + fragmented.run(tasks); + } + + private void appendResolveConflictToContext(final ContinuationContext context) { + context.next(new ResolveConflictInSvn()); + } + + private void appendTailToContextLast(final ContinuationContext context) { + context.last(new ApplyBinaryChanges(), new FinalNotification()); + } + + private List filterOutBinary(List paths) { + List result = null; + for (Iterator iterator = paths.iterator(); iterator.hasNext(); ) { + final Change change = iterator.next(); + if (ChangesUtil.isBinaryChange(change)) { + result = (result == null ? new SmartList() : result); + result.add(change); + iterator.remove(); + } + } + return result; + } + + private class FinalNotification extends TaskDescriptor { + private FinalNotification() { + super("", Where.AWT); + } + + @Override + public void run(ContinuationContext context) { + final StringBuilder message = new StringBuilder().append("Theirs changes merged for ").append(myOldPresentation); + VcsBalloonProblemNotifier.showOverChangesView(myVcs.getProject(), message.toString(), MessageType.INFO); + if (! myWarnings.isEmpty()) { + AbstractVcsHelper.getInstance(myVcs.getProject()).showErrors(myWarnings, TreeConflictRefreshablePanel.TITLE); + } + } + } + + private class ResolveConflictInSvn extends TaskDescriptor { + private ResolveConflictInSvn() { + super("Accepting working state", Where.POOLED); + } + + @Override + public void run(ContinuationContext context) { + try { + new SvnTreeConflictResolver(myVcs, myOldFilePath, myCommittedRevision, null).resolveSelectMineFull(myDescription); + } + catch (VcsException e1) { + context.handleException(e1, false); + } + } + } + + private class ConvertTextPaths extends TaskDescriptor { + private ConvertTextPaths() { + super("", Where.AWT); + } + + @Override + public void run(ContinuationContext context) { + initAddOption(); + List convertedChanges = new SmartList(); + try { + // revision contents is preloaded, so ok to call in awt + convertedChanges = convertPaths(myTheirsChanges); + } + catch (VcsException e) { + context.handleException(e, true); + } + myTheirsChanges.clear(); + myTheirsChanges.addAll(convertedChanges); + } + } + + private class SelectPatchesInApplyPatchDialog extends TaskDescriptor { + private SelectPatchesInApplyPatchDialog() { + super("", Where.AWT); + } + + @Override + public void run(ContinuationContext context) { + final ChangeListManager clManager = ChangeListManager.getInstance(myVcs.getProject()); + final LocalChangeList changeList = clManager.getChangeList(myChange); + final ApplyPatchDifferentiatedDialog dialog = new ApplyPatchDifferentiatedDialog(myVcs.getProject(), + new TreeConflictApplyTheirsPatchExecutor(myVcs, context, myBaseForPatch), + Collections.singletonList(new ApplyPatchSaveToFileExecutor(myVcs.getProject(), myBaseForPatch)), + ApplyPatchMode.APPLY_PATCH_IN_MEMORY, myTextPatches, changeList); + context.suspend(); + dialog.show(); + } + } + + private class TreeConflictApplyTheirsPatchExecutor implements ApplyPatchExecutor { + private final SvnVcs myVcs; + private final ContinuationContext myInner; + private final VirtualFile myBaseDir; + + public TreeConflictApplyTheirsPatchExecutor(SvnVcs vcs, ContinuationContext inner, final VirtualFile baseDir) { + myVcs = vcs; + myInner = inner; + myBaseDir = baseDir; + } + + @Override + public String getName() { + return "Apply patch"; + } + + @Override + public void apply(MultiMap patchGroups, LocalChangeList localList, String fileName, + TransparentlyFailedValue>, PatchSyntaxException> additionalInfo) { + final List patches; + try { + patches = ApplyPatchSaveToFileExecutor.patchGroupsToOneGroup(patchGroups, myBaseDir); + } + catch (IOException e) { + myInner.handleException(e, true); + return; + } + + final PatchApplier patchApplier = + new PatchApplier(myVcs.getProject(), myBaseDir, patches, localList, null, null); + patchApplier.scheduleSelf(false, myInner, true); // 3 + boolean thereAreCreations = false; + for (FilePatch patch : patches) { + if (patch.isNewFile() || ! Comparing.equal(patch.getAfterName(), patch.getBeforeName())) { + thereAreCreations = true; + break; + } + } + if (thereAreCreations) { + // restore deletion of old directory: + myInner.next(new DirectoryAddition()); // 2 + } + appendResolveConflictToContext(myInner); // 1 + appendTailToContextLast(myInner); // 4 + myInner.ping(); + } + } + + private class DirectoryAddition extends TaskDescriptor { + private DirectoryAddition() { + super("Adding " + myOldPresentation + " to Subversion", Where.POOLED); + } + + @Override + public void run(ContinuationContext context) { + try { + myVcs.createWCClient().doAdd(myOldFilePath.getIOFile(), true, true, true, SVNDepth.EMPTY, false, true); + } + catch (SVNException e) { + context.handleException(e, true); + } + } + } + + private class PatchCreator extends TaskDescriptor { + private PatchCreator() { + super("Creating patch for theirs changes", Where.POOLED); + } + + @Override + public void run(ContinuationContext context) { + final Project project = myVcs.getProject(); + final List patches; + try { + patches = IdeaTextPatchBuilder.buildPatch(project, myTheirsChanges, myBaseForPatch.getPath(), false); + myTextPatches = ObjectsConvertor.convert(patches, new Convertor() { + @Override + public TextFilePatch convert(FilePatch o) { + return (TextFilePatch)o; + } + }); + } + catch (VcsException e) { + context.handleException(e, true); + } + } + } + + private class SelectBinaryFiles extends TaskDescriptor { + private SelectBinaryFiles() { + super("", Where.AWT); + } + + @Override + public void run(ContinuationContext context) { + if (myTheirsBinaryChanges.isEmpty()) return; + final List converted; + try { + converted = convertPaths(myTheirsBinaryChanges); + } + catch (VcsException e) { + context.handleException(e, true); + return; + } + if (converted.isEmpty()) return; + final Map map = new HashMap(); + for (Change change : converted) { + map.put(ChangesUtil.getFilePath(change), change); + } + final Collection selected = chooseBinaryFiles(converted, map.keySet()); + myTheirsBinaryChanges.clear(); + for (FilePath filePath : selected) { + myTheirsBinaryChanges.add(map.get(filePath)); + } + } + } + + private class ApplyBinaryChanges extends TaskDescriptor { + private ApplyBinaryChanges() { + super("", Where.AWT); + } + + @Override + public void run(final ContinuationContext context) { + if (myTheirsBinaryChanges.isEmpty()) return; + final Application application = ApplicationManager.getApplication(); + final VcsException[] exc = new VcsException[1]; + final List dirtyPaths = new ArrayList(); + for (final Change change : myTheirsBinaryChanges) { + application.runWriteAction(new Runnable() { + public void run() { + try { + if (change.getAfterRevision() != null) { + final FilePath file = change.getAfterRevision().getFile(); + dirtyPaths.add(file); + final String parentPath = file.getParentPath().getPath(); + final VirtualFile parentFile = VfsUtil.createDirectoryIfMissing(parentPath); + if (parentFile == null) { + context.handleException(new VcsException("Can not create directory: " + parentPath, true), false); + return; + } + final VirtualFile child = parentFile.createChildData(TreeConflictRefreshablePanel.class, file.getName()); + if (child == null) { + context.handleException(new VcsException("Can not create file: " + file.getPath(), true), false); + return; + } + child.setBinaryContent(((BinaryContentRevision) change.getAfterRevision()).getBinaryContent()); + } else { + final FilePath path = change.getBeforeRevision().getFile(); + dirtyPaths.add(path); + final VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(path.getIOFile()); + if (file == null) { + context.handleException(new VcsException("Can not delete file: " + file.getPath(), true), false); + return; + } + file.delete(TreeConflictRefreshablePanel.class); + } + } + catch (IOException e) { + exc[0] = new VcsException(e); + } + catch (VcsException e) { + exc[0] = e; + } + } + }); + if (exc[0] != null) { + context.handleException(exc[0], true); + return; + } + } + VcsDirtyScopeManager.getInstance(myVcs.getProject()).filePathsDirty(dirtyPaths, null); + } + } + + private Collection chooseBinaryFiles(List converted, Set paths) { + String singleMessage = ""; + if (paths.size() == 1) { + final Change change = converted.get(0); + final FileStatus status = change.getFileStatus(); + final FilePath path = ChangesUtil.getFilePath(change); + final String stringPath = TreeConflictRefreshablePanel.filePath(path); + if (FileStatus.DELETED.equals(status)) { + singleMessage = "Delete binary file " + stringPath + " (according to theirs changes)?"; + } else if (FileStatus.ADDED.equals(status)) { + singleMessage = "Create binary file " + stringPath + " (according to theirs changes)?"; + } else { + singleMessage = "Apply changes to binary file " + stringPath + " (according to theirs changes)?"; + } + } + return AbstractVcsHelper.getInstance(myVcs.getProject()).selectFilePathsToProcess(new ArrayList(paths), + TreeConflictRefreshablePanel.TITLE, "Select binary files to patch", TreeConflictRefreshablePanel.TITLE, + singleMessage, new VcsShowConfirmationOption() { + + @Override + public Value getValue() { + return null; + } + + @Override + public void setValue(Value value) { + } + + @Override + public boolean isPersistent() { + return false; + } + }); + } + + private List convertPaths(List changesForPatch) throws VcsException { + initAddOption(); + final List changes = new ArrayList(); + for (Change change : changesForPatch) { + if (! isUnderOldDir(change, myOldFilePath)) continue; + ContentRevision before = null; + ContentRevision after = null; + if (change.getBeforeRevision() != null) { + before = new SimpleContentRevision(change.getBeforeRevision().getContent(), + rebasePath(myOldFilePath, myNewFilePath, change.getBeforeRevision().getFile()), + change.getBeforeRevision().getRevisionNumber().asString()); + } + if (change.getAfterRevision() != null) { + // if addition or move - do not move to the new path + if (myAdd && (change.getBeforeRevision() == null || change.isMoved() || change.isRenamed())) { + after = change.getAfterRevision(); + } else { + after = new SimpleContentRevision(change.getAfterRevision().getContent(), + rebasePath(myOldFilePath, myNewFilePath, change.getAfterRevision().getFile()), + change.getAfterRevision().getRevisionNumber().asString()); + } + } + changes.add(new Change(before, after)); + } + return changes; + } + + private boolean isUnderOldDir(Change change, FilePath path) { + if (change.getBeforeRevision() != null) { + final boolean isUnder = FileUtil.isAncestor(path.getIOFile(), change.getBeforeRevision().getFile().getIOFile(), true); + if (isUnder) { + return true; + } + } + if (change.getAfterRevision() != null) { + final boolean isUnder = FileUtil.isAncestor(path.getIOFile(), change.getAfterRevision().getFile().getIOFile(), true); + if (isUnder) { + return isUnder; + } + } + return false; + } + + private FilePath rebasePath(final FilePath oldBase, final FilePath newBase, final FilePath path) { + final String relativePath = FileUtil.getRelativePath(oldBase.getPath(), path.getPath(), File.separatorChar); + //if (StringUtil.isEmptyOrSpaces(relativePath)) return path; + return ((FilePathImpl) newBase).createChild(relativePath, path.isDirectory()); + } + + private class PreloadChangesContentsForFile extends TaskDescriptor { + private PreloadChangesContentsForFile() { + super("Getting base and theirs revisions content", Where.POOLED); + } + + @Override + public void run(ContinuationContext context) { + final SvnContentRevision base = SvnContentRevision.createBaseRevision(myVcs, myNewFilePath, myCommittedRevision.getRevision()); + final SvnContentRevision remote = SvnContentRevision.createRemote(myVcs, myOldFilePath, SVNRevision.create( + myDescription.getSourceRightVersion().getPegRevision())); + try { + final ContentRevision newBase = new SimpleContentRevision(base.getContent(), myNewFilePath, base.getRevisionNumber().asString()); + final ContentRevision newRemote = new SimpleContentRevision(remote.getContent(), myNewFilePath, remote.getRevisionNumber().asString()); + myTheirsChanges.add(new Change(newBase, newRemote)); + } + catch (VcsException e) { + context.handleException(e, true); + } + } + } + + private class PreloadChangesContentsForDir extends TaskDescriptor { + private PreloadChangesContentsForDir() { + super("Getting base and theirs revisions content", Where.POOLED); + } + + @Override + public void run(ContinuationContext context) { + final List changesForPatch; + try { + final List lst = loadSvnChangeListsForPatch(myDescription); + changesForPatch = CommittedChangesTreeBrowser.collectChanges(lst, true); + for (Change change : changesForPatch) { + if (change.getBeforeRevision() != null) { + preloadRevisionContents(change.getBeforeRevision()); + } + if (change.getAfterRevision() != null) { + preloadRevisionContents(change.getAfterRevision()); + } + } + } + catch (VcsException e) { + context.handleException(e, true); + return; + } + final List binaryChanges = filterOutBinary(changesForPatch); + if (binaryChanges != null && ! binaryChanges.isEmpty()) { + myTheirsBinaryChanges.addAll(binaryChanges); + } + if (! changesForPatch.isEmpty()) { + myTheirsChanges.addAll(changesForPatch); + } + } + } + + private void preloadRevisionContents(ContentRevision cr) throws VcsException { + if (cr instanceof BinaryContentRevision) { + ((BinaryContentRevision) cr).getBinaryContent(); + } else { + cr.getContent(); + } + } + + private List loadSvnChangeListsForPatch(SVNTreeConflictDescription description) throws VcsException { + long max = description.getSourceRightVersion().getPegRevision(); + long min = description.getSourceLeftVersion().getPegRevision(); + + final ChangeBrowserSettings settings = new ChangeBrowserSettings(); + settings.USE_CHANGE_BEFORE_FILTER = settings.USE_CHANGE_AFTER_FILTER = true; + settings.CHANGE_BEFORE = "" + max; + settings.CHANGE_AFTER = "" + min; + final List committedChanges = myVcs.getCachingCommittedChangesProvider().getCommittedChanges( + settings, new SvnRepositoryLocation(description.getSourceRightVersion().getRepositoryRoot().toString()), 0); + final List lst = new ArrayList(committedChanges.size() - 1); + for (SvnChangeList change : committedChanges) { + if (change.getNumber() == min) { + continue; + } + lst.add(change); + } + return lst; + } + + private void initAddOption() { + ApplicationManager.getApplication().assertIsDispatchThread(); + if (myAdd == null) { + myAdd = getAddedFilesPlaceOption(); + } + } + + private boolean getAddedFilesPlaceOption() { + final SvnConfiguration configuration = SvnConfiguration.getInstance(myVcs.getProject()); + boolean add = Boolean.TRUE.equals(configuration.TREE_CONFLICT_MERGE_THEIRS_NEW_INTO_OLD_PLACE); + if (configuration.TREE_CONFLICT_MERGE_THEIRS_NEW_INTO_OLD_PLACE == null) { + if (! containAdditions(myTheirsChanges) && ! containAdditions(myTheirsBinaryChanges)) return false; + final int i = Messages.showYesNoDialog("Keep newly created file(s) in their original place?", TreeConflictRefreshablePanel.TITLE, "Keep", "Move", + Messages.getQuestionIcon(), new DialogWrapper.DoNotAskOption() { + @Override + public boolean isToBeShown() { + return true; + } + + @Override + public void setToBeShown(boolean value, int exitCode) { + if (!value) { + if (exitCode == 0) { + // yes + configuration.TREE_CONFLICT_MERGE_THEIRS_NEW_INTO_OLD_PLACE = true; + } + else { + configuration.TREE_CONFLICT_MERGE_THEIRS_NEW_INTO_OLD_PLACE = false; + } + } + } + + @Override + public boolean canBeHidden() { + return true; + } + + @Override + public boolean shouldSaveOptionsOnCancel() { + return true; + } + + @Override + public String getDoNotShowMessage() { + return CommonBundle.message("dialog.options.do.not.ask"); + } + }); + add = Messages.YES == i; + } + return add; + } + + private boolean containAdditions(final List changes) { + boolean addFound = false; + for (Change change : changes) { + if (change.getBeforeRevision() == null || change.isMoved() || change.isRenamed()) { + addFound = true; + break; + } + } + return addFound; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/TreeConflictRefreshablePanel.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/TreeConflictRefreshablePanel.java index 5497c90cff6d..fec9c72b402b 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/TreeConflictRefreshablePanel.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/TreeConflictRefreshablePanel.java @@ -16,10 +16,9 @@ package org.jetbrains.idea.svn.treeConflict; import com.intellij.openapi.Disposable; +import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diff.impl.patch.BinaryFilePatch; -import com.intellij.openapi.diff.impl.patch.FilePatch; -import com.intellij.openapi.diff.impl.patch.IdeaTextPatchBuilder; +import com.intellij.openapi.diff.impl.patch.*; import com.intellij.openapi.diff.impl.patch.formove.PatchApplier; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.progress.BackgroundTaskQueue; @@ -36,16 +35,16 @@ import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.FilePathImpl; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.*; -import com.intellij.openapi.vcs.changes.committed.CommittedChangesTreeBrowser; +import com.intellij.openapi.vcs.changes.patch.*; import com.intellij.openapi.vcs.history.*; import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier; import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings; import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.BeforeAfter; -import com.intellij.util.Consumer; import com.intellij.util.SmartList; import com.intellij.util.containers.Convertor; +import com.intellij.util.containers.MultiMap; import com.intellij.util.continuation.*; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.VcsBackgroundTask; @@ -403,87 +402,7 @@ public class TreeConflictRefreshablePanel extends AbstractRefreshablePanel { return new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - final FilePath oldFilePath = myChange.getBeforeRevision().getFile(); - final FilePath newFilePath = myChange.getAfterRevision().getFile(); - int ok = Messages.showOkCancelDialog(myVcs.getProject(), - (myChange.isMoved() ? - SvnBundle.message("confirmation.resolve.tree.conflict.merge.moved", filePath(oldFilePath), - filePath(newFilePath)) : - SvnBundle.message("confirmation.resolve.tree.conflict.merge.renamed", filePath(oldFilePath), - filePath(newFilePath))), - TITLE, Messages.getQuestionIcon()); - if (Messages.OK != ok) return; - - FileDocumentManager.getInstance().saveAllDocuments(); - final String name = "Merge changes from theirs for: " + filePath(oldFilePath); - - final GatheringContinuationContext cc = new GatheringContinuationContext(); - cc.addExceptionHandler(VcsException.class, new Consumer() { - @Override - public void consume(VcsException e) { - AbstractVcsHelper.getInstance(myVcs.getProject()).showErrors(Collections.singletonList(e), name); - } - }); - cc.next(new TaskDescriptor("Creating patch for theirs changes", Where.POOLED) { - @Override - public void run(ContinuationContext context) { - try { - ProgressManager.progress("Getting base and theirs revisions content"); - final List changes = new SmartList(); - - if (SVNNodeKind.DIR.equals(description.getNodeKind())) { - long max = description.getSourceRightVersion().getPegRevision(); - long min = description.getSourceLeftVersion().getPegRevision(); - - final ChangeBrowserSettings settings = new ChangeBrowserSettings(); - settings.USE_CHANGE_BEFORE_FILTER = settings.USE_CHANGE_AFTER_FILTER = true; - settings.CHANGE_BEFORE = "" + max; - settings.CHANGE_AFTER = "" + min; - final List committedChanges = myVcs.getCachingCommittedChangesProvider().getCommittedChanges( - settings, new SvnRepositoryLocation(description.getSourceRightVersion().getRepositoryRoot().toString()), 0); - final List lst = new ArrayList(committedChanges.size() - 1); - for (SvnChangeList change : committedChanges) { - if (change.getNumber() == min) { - continue; - } - lst.add(change); - } - final List changesForPatch = CommittedChangesTreeBrowser.collectChanges(lst, true); - for (Change change : changesForPatch) { - if (! isUnderOldDir(change, oldFilePath)) continue; - ContentRevision before = null; - ContentRevision after = null; - if (change.getBeforeRevision() != null) { - before = new SimpleContentRevision(change.getBeforeRevision().getContent(), - rebasePath(oldFilePath, newFilePath, change.getBeforeRevision().getFile()), - change.getBeforeRevision().getRevisionNumber().asString()); - } - if (change.getAfterRevision() != null) { - after = new SimpleContentRevision(change.getAfterRevision().getContent(), - rebasePath(oldFilePath, newFilePath, change.getAfterRevision().getFile()), - change.getAfterRevision().getRevisionNumber().asString()); - } - changes.add(new Change(before, after)); - } - } else { - final SvnContentRevision base = SvnContentRevision.createBaseRevision(myVcs, newFilePath, myCommittedRevision.getRevision()); - final SvnContentRevision remote = SvnContentRevision.createRemote(myVcs, oldFilePath, - SVNRevision.create( - description.getSourceRightVersion().getPegRevision())); - final ContentRevision newBase = new SimpleContentRevision(base.getContent(), newFilePath, base.getRevisionNumber().asString()); - final ContentRevision newRemote = new SimpleContentRevision(remote.getContent(), newFilePath, remote.getRevisionNumber().asString()); - changes.add(new Change(newBase, newRemote)); - } - - mergeFromTheirs(context, newFilePath, oldFilePath, description, changes); - } - catch (VcsException e1) { - context.handleException(e1); - } - } - }); - final Continuation fragmented = Continuation.createFragmented(myVcs.getProject(), false); - fragmented.run(cc.getList()); + new MergeFromTheirsResolver(myVcs, description, myChange, myCommittedRevision).execute(); } }; } @@ -510,44 +429,6 @@ public class TreeConflictRefreshablePanel extends AbstractRefreshablePanel { return ((FilePathImpl) newBase).createChild(relativePath, path.isDirectory()); } - private void mergeFromTheirs(ContinuationContext context, final FilePath newFilePath, final FilePath oldFilePath, - final SVNTreeConflictDescription description, final List changes) throws VcsException { - - ProgressManager.progress("Creating patch for theirs changes"); - final VirtualFile baseForPatch = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public VirtualFile compute() { - return ChangesUtil.findValidParent(newFilePath); - } - }); - final Project project = myVcs.getProject(); - final List patches = IdeaTextPatchBuilder.buildPatch(project, changes, baseForPatch.getPath(), false); - - ProgressManager.progress("Applying patch to " + newFilePath.getPath()); - final ChangeListManager clManager = ChangeListManager.getInstance(project); - final LocalChangeList changeList = clManager.getChangeList(myChange); - final PatchApplier patchApplier = - new PatchApplier(project, baseForPatch, patches, changeList, null, null); - patchApplier.scheduleSelf(false, context, true); - context.last(new TaskDescriptor("Accepting working state", Where.POOLED) { - @Override - public void run(ContinuationContext context) { - try { - new SvnTreeConflictResolver(myVcs, oldFilePath, myCommittedRevision, null).resolveSelectMineFull(description); - } - catch (VcsException e1) { - context.handleException(e1); - } - } - }); - context.last(new TaskDescriptor("", Where.AWT) { - @Override - public void run(ContinuationContext context) { - VcsBalloonProblemNotifier.showOverChangesView(myVcs.getProject(), "Theirs changes merged for " + filePath(myPath), MessageType.INFO); - } - }); - } - public static String filePath(FilePath newFilePath) { return newFilePath.getName() + " (" + diff --git a/plugins/svn4idea/svn4idea.iml b/plugins/svn4idea/svn4idea.iml index 4459e1cfa32c..34af8777bc56 100644 --- a/plugins/svn4idea/svn4idea.iml +++ b/plugins/svn4idea/svn4idea.iml @@ -46,7 +46,7 @@ - +