mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[patch]: create patch content viewer and diff tool
* show patch content as a diff if no local file was found for shelved change (IDEA-166238, IDEA-155689);
This commit is contained in:
@@ -34,6 +34,7 @@
|
||||
|
||||
<diff.merge.MergeTool implementation="com.intellij.openapi.vcs.changes.patch.tool.ApplyPatchMergeTool"/>
|
||||
<diff.DiffTool implementation="com.intellij.openapi.vcs.changes.patch.tool.ApplyPatchDiffTool"/>
|
||||
<diff.DiffTool implementation="com.intellij.openapi.vcs.changes.patch.tool.PatchDiffTool"/>
|
||||
|
||||
<selectInTarget implementation="com.intellij.openapi.vcs.changes.SelectInChangesViewTarget"/>
|
||||
|
||||
|
||||
+43
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<PatchChangeBuilder.Hunk> 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<PatchChangeBuilder.Hunk> {
|
||||
@NotNull
|
||||
@Override
|
||||
protected List<PatchChangeBuilder.Hunk> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
-5
@@ -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<String, List<TextFilePatch>> myFilePatchesMap;
|
||||
private final Project myProject;
|
||||
|
||||
Reference in New Issue
Block a user