IDEA-97493 diff: support "Create Patch" action from DiffViewer
[idea/community.git] / platform / vcs-impl / src / com / intellij / openapi / vcs / changes / actions / diff / DiffViewerCreatePatchActionProvider.java
1 // Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.openapi.vcs.changes.actions.diff;
3
4 import com.intellij.codeInsight.daemon.OutsidersPsiFileSupport;
5 import com.intellij.diff.DiffVcsDataKeys;
6 import com.intellij.diff.contents.DiffContent;
7 import com.intellij.diff.contents.DocumentContent;
8 import com.intellij.diff.contents.EmptyContent;
9 import com.intellij.diff.contents.FileContent;
10 import com.intellij.diff.requests.ContentDiffRequest;
11 import com.intellij.diff.requests.DiffRequest;
12 import com.intellij.diff.tools.util.DiffDataKeys;
13 import com.intellij.diff.util.DiffUserDataKeysEx;
14 import com.intellij.openapi.actionSystem.AnActionEvent;
15 import com.intellij.openapi.actionSystem.AnActionExtensionProvider;
16 import com.intellij.openapi.diff.DiffBundle;
17 import com.intellij.openapi.fileTypes.FileType;
18 import com.intellij.openapi.util.Pair;
19 import com.intellij.openapi.util.text.StringUtil;
20 import com.intellij.openapi.vcs.FilePath;
21 import com.intellij.openapi.vcs.changes.Change;
22 import com.intellij.openapi.vcs.changes.ContentRevision;
23 import com.intellij.openapi.vcs.changes.CurrentContentRevision;
24 import com.intellij.openapi.vcs.changes.SimpleContentRevision;
25 import com.intellij.openapi.vcs.changes.actions.CreatePatchFromChangesAction;
26 import com.intellij.openapi.vcs.history.VcsRevisionNumber;
27 import com.intellij.openapi.vfs.VirtualFile;
28 import com.intellij.util.ObjectUtils;
29 import com.intellij.util.PathUtil;
30 import com.intellij.vcsUtil.VcsUtil;
31 import org.jetbrains.annotations.Nls;
32 import org.jetbrains.annotations.NotNull;
33 import org.jetbrains.annotations.Nullable;
34
35 import java.util.Collections;
36 import java.util.List;
37 import java.util.Objects;
38
39 public class DiffViewerCreatePatchActionProvider implements AnActionExtensionProvider {
40   private final boolean mySilentClipboard;
41
42   private DiffViewerCreatePatchActionProvider(boolean silentClipboard) {
43     mySilentClipboard = silentClipboard;
44   }
45
46   public static class Dialog extends DiffViewerCreatePatchActionProvider {
47     public Dialog() {
48       super(false);
49     }
50   }
51
52   public static class Clipboard extends DiffViewerCreatePatchActionProvider {
53     public Clipboard() {
54       super(true);
55     }
56   }
57
58   @Override
59   public boolean isActive(@NotNull AnActionEvent e) {
60     return e.getData(DiffDataKeys.DIFF_VIEWER) != null;
61   }
62
63   @Override
64   public void update(@NotNull AnActionEvent e) {
65     DiffRequest request = e.getData(DiffDataKeys.DIFF_REQUEST);
66     boolean isEnabled = request != null && isSupported(request);
67     e.getPresentation().setEnabledAndVisible(isEnabled);
68   }
69
70   @Override
71   public void actionPerformed(@NotNull AnActionEvent e) {
72     DiffRequest request = Objects.requireNonNull(e.getData(DiffDataKeys.DIFF_REQUEST));
73     Change change = createChange(request);
74     CreatePatchFromChangesAction.createPatch(e.getProject(), null, Collections.singletonList(change), mySilentClipboard);
75   }
76
77   private static boolean isSupported(@NotNull DiffRequest request) {
78     if (request.getUserData(ChangeDiffRequestProducer.CHANGE_KEY) != null) return true;
79
80     ContentDiffRequest contentRequest = ObjectUtils.tryCast(request, ContentDiffRequest.class);
81     if (contentRequest == null) return false;
82
83     List<DiffContent> contents = contentRequest.getContents();
84     if (contents.size() != 2) return false;
85
86     for (DiffContent content : contents) {
87       boolean canShow = content instanceof DocumentContent ||
88                         content instanceof EmptyContent;
89       if (!canShow) return false;
90     }
91     return true;
92   }
93
94   @NotNull
95   private static Change createChange(@NotNull DiffRequest request) {
96     Change change = request.getUserData(ChangeDiffRequestProducer.CHANGE_KEY);
97     if (change != null) return change;
98
99     List<DiffContent> contents = ((ContentDiffRequest)request).getContents();
100     List<@Nls String> titles = ((ContentDiffRequest)request).getContentTitles();
101     assert contents.size() == 2;
102     String bTitle = ObjectUtils.chooseNotNull(titles.get(0), DiffBundle.message("diff.version.title.before"));
103     String aTitle = ObjectUtils.chooseNotNull(titles.get(1), DiffBundle.message("diff.version.title.after"));
104     ContentRevision bRev = createRevision(contents.get(0), bTitle);
105     ContentRevision aRev = createRevision(contents.get(1), aTitle);
106     return new Change(bRev, aRev);
107   }
108
109   @Nullable
110   private static ContentRevision createRevision(@NotNull DiffContent content, @NotNull String title) {
111     if (content instanceof EmptyContent) return null;
112     if (content instanceof FileContent) {
113       VirtualFile file = ((FileContent)content).getFile();
114       if (file.isInLocalFileSystem()) {
115         return new CurrentContentRevision(VcsUtil.getFilePath(file));
116       }
117     }
118     if (content instanceof DocumentContent) {
119       String text = ((DocumentContent)content).getDocument().getText();
120
121       Pair<FilePath, VcsRevisionNumber> info = content.getUserData(DiffVcsDataKeys.REVISION_INFO);
122       if (info != null) {
123         return new SimpleContentRevision(text, info.first, info.second.asString());
124       }
125
126       return new SimpleContentRevision(text, guessFilePath(content, title), "");
127     }
128     throw new IllegalStateException(content.toString());
129   }
130
131   @NotNull
132   private static FilePath guessFilePath(@NotNull DiffContent content, @NotNull String title) {
133     if (content instanceof FileContent) {
134       VirtualFile file = ((FileContent)content).getFile();
135       String path = OutsidersPsiFileSupport.getOriginalFilePath(file);
136       if (path != null) return VcsUtil.getFilePath(path);
137     }
138
139     String fileName = content.getUserData(DiffUserDataKeysEx.FILE_NAME);
140     if (fileName == null && content instanceof DocumentContent) {
141       VirtualFile highlightFile = ((DocumentContent)content).getHighlightFile();
142       fileName = highlightFile != null ? highlightFile.getName() : null;
143     }
144     if (fileName != null) return VcsUtil.getFilePath(fileName);
145
146     FileType fileType = content.getContentType();
147     String ext = fileType != null ? fileType.getDefaultExtension() : null;
148     if (StringUtil.isEmptyOrSpaces(ext)) ext = "tmp";
149
150     String path = PathUtil.suggestFileName(title + "." + ext, true, false);
151     return VcsUtil.getFilePath(path);
152   }
153 }