diff --git a/platform/platform-resources/src/META-INF/VcsExtensions.xml b/platform/platform-resources/src/META-INF/VcsExtensions.xml
index c22b90ae306f..157d455862e6 100644
--- a/platform/platform-resources/src/META-INF/VcsExtensions.xml
+++ b/platform/platform-resources/src/META-INF/VcsExtensions.xml
@@ -34,6 +34,7 @@
+
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/tool/PatchDiffRequest.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/tool/PatchDiffRequest.java
new file mode 100644
index 000000000000..332c1d49c945
--- /dev/null
+++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/tool/PatchDiffRequest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2000-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.openapi.vcs.changes.patch.tool;
+
+import com.intellij.diff.requests.DiffRequest;
+import com.intellij.openapi.vcs.changes.patch.AppliedTextPatch;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class PatchDiffRequest extends DiffRequest {
+ @NotNull private final AppliedTextPatch myAppliedPatch;
+
+ @Nullable private final String myWindowTitle;
+
+ public PatchDiffRequest(@NotNull AppliedTextPatch patch, @Nullable String windowTitle) {
+ myAppliedPatch = patch;
+ myWindowTitle = windowTitle;
+ }
+
+ @Nullable
+ @Override
+ public String getTitle() {
+ return myWindowTitle;
+ }
+
+ @NotNull
+ public AppliedTextPatch getPatch() {
+ return myAppliedPatch;
+ }
+}
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/tool/PatchDiffTool.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/tool/PatchDiffTool.java
new file mode 100644
index 000000000000..757dfc849d25
--- /dev/null
+++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/tool/PatchDiffTool.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2000-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.openapi.vcs.changes.patch.tool;
+
+import com.intellij.diff.DiffContext;
+import com.intellij.diff.FrameDiffTool;
+import com.intellij.diff.requests.DiffRequest;
+import com.intellij.diff.tools.util.DiffDataKeys;
+import com.intellij.diff.tools.util.PrevNextDifferenceIterableBase;
+import com.intellij.diff.tools.util.SimpleDiffPanel;
+import com.intellij.diff.util.DiffDrawUtil;
+import com.intellij.diff.util.DiffUtil;
+import com.intellij.diff.util.LineRange;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.actionSystem.DataProvider;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.EditorFactory;
+import com.intellij.openapi.editor.ex.EditorEx;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.BooleanGetter;
+import com.intellij.openapi.vcs.VcsBundle;
+import com.intellij.ui.components.panels.Wrapper;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.List;
+
+public class PatchDiffTool implements FrameDiffTool {
+ @NotNull
+ @Override
+ public String getName() {
+ return "Patch content viewer";
+ }
+
+ @Override
+ public boolean canShow(@NotNull DiffContext context, @NotNull DiffRequest request) {
+ return request instanceof PatchDiffRequest;
+ }
+
+ @NotNull
+ @Override
+ public DiffViewer createComponent(@NotNull DiffContext context, @NotNull DiffRequest request) {
+ return new MyPatchViewer(context, (PatchDiffRequest)request);
+ }
+
+ private static class MyPatchViewer implements DiffViewer, DataProvider {
+
+
+ private final Project myProject;
+ private final SimpleDiffPanel myPanel;
+ private final EditorEx myEditor;
+ private final DiffContext myContext;
+ private final PatchDiffRequest myRequest;
+ private final MyPrevNextDifferenceIterable myPrevNextDifferenceIterable;
+ private final List myHunks = new ArrayList<>();
+
+ public MyPatchViewer(DiffContext context, PatchDiffRequest request) {
+ myProject = context.getProject();
+ myContext = context;
+ myRequest = request;
+ Document document = EditorFactory.getInstance().createDocument("");
+ myEditor = DiffUtil.createEditor(document, myProject, true, true);
+ myPrevNextDifferenceIterable = new MyPrevNextDifferenceIterable();
+
+ Wrapper editorPanel = new Wrapper(new BorderLayout(0, DiffUtil.TITLE_GAP), myEditor.getComponent());
+ editorPanel.add(DiffUtil.createTitle(VcsBundle.message("patch.apply.conflict.patch")), BorderLayout.NORTH);
+ myPanel = new SimpleDiffPanel(editorPanel, this, context);
+ }
+
+ @NotNull
+ @Override
+ public JComponent getComponent() {
+ return myPanel;
+ }
+
+ @Nullable
+ @Override
+ public JComponent getPreferredFocusedComponent() {
+ return myEditor.getContentComponent();
+ }
+
+ @NotNull
+ @Override
+ public ToolbarComponents init() {
+ myPanel.setPersistentNotifications(DiffUtil.getCustomNotifications(myContext, myRequest));
+ onInit();
+ return new FrameDiffTool.ToolbarComponents();
+ }
+
+ @Override
+ public void dispose() {
+ EditorFactory.getInstance().releaseEditor(myEditor);
+ }
+
+ private void onInit() {
+ PatchChangeBuilder builder = new PatchChangeBuilder();
+ builder.exec(myRequest.getPatch().getHunks());
+ myHunks.addAll(builder.getHunks());
+
+ Document patchDocument = myEditor.getDocument();
+ WriteAction.run(() -> patchDocument.setText(builder.getPatchContent().toString()));
+
+ myEditor.getGutterComponentEx()
+ .setLineNumberConvertor(builder.getLineConvertor1().createConvertor(), builder.getLineConvertor2().createConvertor());
+
+ for (int line : builder.getSeparatorLines().toNativeArray()) {
+ int offset = patchDocument.getLineStartOffset(line);
+ DiffDrawUtil.createLineSeparatorHighlighter(myEditor, offset, offset, BooleanGetter.TRUE);
+ }
+ // highlighting
+ for (PatchChangeBuilder.Hunk hunk : myHunks) {
+ DiffDrawUtil.createUnifiedChunkHighlighters(myEditor, hunk.getPatchDeletionRange(), hunk.getPatchInsertionRange(), null);
+ }
+ }
+
+ @Nullable
+ @Override
+ public Object getData(@NonNls String dataId) {
+ if (CommonDataKeys.PROJECT.is(dataId)) return myProject;
+ if (DiffDataKeys.PREV_NEXT_DIFFERENCE_ITERABLE.is(dataId)) return myPrevNextDifferenceIterable;
+ if (DiffDataKeys.CURRENT_EDITOR.is(dataId)) return myEditor;
+ if (DiffDataKeys.CURRENT_CHANGE_RANGE.is(dataId)) {
+ return myPrevNextDifferenceIterable.getHunkRangeByLine(myEditor.getCaretModel().getLogicalPosition().line);
+ }
+ return null;
+ }
+
+ private class MyPrevNextDifferenceIterable extends PrevNextDifferenceIterableBase {
+ @NotNull
+ @Override
+ protected List getChanges() {
+ return myHunks;
+ }
+
+ @NotNull
+ @Override
+ protected EditorEx getEditor() {
+ return myEditor;
+ }
+
+ @Override
+ protected int getStartLine(@NotNull PatchChangeBuilder.Hunk change) {
+ return change.getPatchDeletionRange().start;
+ }
+
+ @Override
+ protected int getEndLine(@NotNull PatchChangeBuilder.Hunk change) {
+ return change.getPatchInsertionRange().end;
+ }
+
+ @Nullable
+ LineRange getHunkRangeByLine(int line) {
+ for (PatchChangeBuilder.Hunk hunk : getChanges()) {
+ int start = hunk.getPatchDeletionRange().start;
+ int end = hunk.getPatchInsertionRange().end;
+ if (start <= line && end > line) {
+ return new LineRange(start, end);
+ }
+ if (start > line) return null;
+ }
+ return null;
+ }
+ }
+ }
+}
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesAction.java
index 34f02a38687a..67d8ca187ea5 100644
--- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesAction.java
+++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesAction.java
@@ -26,7 +26,6 @@ import com.intellij.diff.contents.DiffContent;
import com.intellij.diff.requests.DiffRequest;
import com.intellij.diff.requests.SimpleDiffRequest;
import com.intellij.diff.requests.UnknownFileTypeDiffRequest;
-import com.intellij.diff.tools.util.DiffNotifications;
import com.intellij.diff.util.DiffUtil;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
@@ -34,6 +33,7 @@ import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.diff.impl.patch.*;
import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchBase;
+import com.intellij.openapi.diff.impl.patch.apply.GenericPatchApplier;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileTypes.UnknownFileType;
import com.intellij.openapi.progress.ProcessCanceledException;
@@ -51,7 +51,9 @@ import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vcs.changes.CommitContext;
import com.intellij.openapi.vcs.changes.FilePathsHelper;
import com.intellij.openapi.vcs.changes.actions.diff.ChangeGoToChangePopupAction;
+import com.intellij.openapi.vcs.changes.patch.AppliedTextPatch;
import com.intellij.openapi.vcs.changes.patch.ApplyPatchForBaseRevisionTexts;
+import com.intellij.openapi.vcs.changes.patch.tool.PatchDiffRequest;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import com.intellij.util.containers.ContainerUtil;
@@ -64,6 +66,7 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.*;
+import static com.intellij.diff.tools.util.DiffNotifications.createNotification;
import static com.intellij.openapi.vcs.changes.patch.PatchDiffRequestFactory.createConflictDiffRequest;
import static com.intellij.openapi.vcs.changes.patch.PatchDiffRequestFactory.createDiffRequest;
import static com.intellij.util.ObjectUtils.assertNotNull;
@@ -188,7 +191,16 @@ public class DiffShelvedChangesAction extends AnAction implements DumbAware {
@Override
public DiffRequest process(@NotNull UserDataHolder context, @NotNull ProgressIndicator indicator)
throws DiffRequestProducerException, ProcessCanceledException {
- throw new DiffRequestProducerException("Cannot find base for '" + (beforePath != null ? beforePath : afterPath) + "'");
+ try {
+ TextFilePatch patch = preloader.getPatch(shelvedChange, new CommitContext());
+ PatchDiffRequest patchDiffRequest = new PatchDiffRequest(createAppliedTextPatch(patch), getName());
+ DiffUtil.addNotification(createNotification("Cannot find local file for '" + chooseNotNull(beforePath, afterPath) + "'"),
+ patchDiffRequest);
+ return patchDiffRequest;
+ }
+ catch (VcsException e) {
+ throw new DiffRequestProducerException("Can't show diff for '" + getName() + "'", e);
+ }
}
});
continue;
@@ -261,9 +273,7 @@ public class DiffShelvedChangesAction extends AnAction implements DumbAware {
? createConflictDiffRequest(project, file, patch, SHELVED_VERSION, texts, getName())
: createDiffRequest(project, shelvedChange.getChange(project), getName(), context, indicator);
if (!withLocal) {
- DiffUtil
- .addNotification(DiffNotifications.createNotification(DIFF_WITH_BASE_ERROR + " Showing difference with local version"),
- diffRequest);
+ DiffUtil.addNotification(createNotification(DIFF_WITH_BASE_ERROR + " Showing difference with local version"), diffRequest);
}
return diffRequest;
}
@@ -272,6 +282,16 @@ public class DiffShelvedChangesAction extends AnAction implements DumbAware {
}
}
+ /**
+ * Simple way to reuse patch parser from GPA -> apply onto empty text
+ */
+ @NotNull
+ private static AppliedTextPatch createAppliedTextPatch(@NotNull TextFilePatch patch) {
+ final GenericPatchApplier applier = new GenericPatchApplier("", patch.getHunks());
+ applier.execute();
+ return AppliedTextPatch.create(applier.getAppliedInfo());
+ }
+
private static class PatchesPreloader {
private final Map> myFilePatchesMap;
private final Project myProject;