diff: svn: implement diff with properties

This commit is contained in:
Aleksey Pivovarov
2015-02-03 20:13:40 +03:00
parent 289a8306f2
commit 817dbac97d
17 changed files with 1329 additions and 45 deletions
+20 -8
View File
@@ -32,14 +32,16 @@
</group>
<action id="IntegrateChangeSetAction" class="org.jetbrains.idea.svn.actions.IntegrateChangeSetAction" popup="true" icon="SvnIcons.IntegrateToBranch">
<action id="IntegrateChangeSetAction" class="org.jetbrains.idea.svn.actions.IntegrateChangeSetAction" popup="true"
icon="SvnIcons.IntegrateToBranch">
<add-to-group group-id="RepositoryChangesBrowserToolbar" anchor="last"/>
</action>
<action id="PropertiesDiff" class="org.jetbrains.idea.svn.actions.ShowPropertiesDiffAction" popup="true" icon="SvnIcons.PropertiesDiff">
<add-to-group group-id="RepositoryChangesBrowserToolbar" anchor="last"/>
</action>
<action id="EditCommitMessage" class="org.jetbrains.idea.svn.history.SvnEditCommitMessageAction" popup="true" icon="AllIcons.Actions.Edit"
<action id="EditCommitMessage" class="org.jetbrains.idea.svn.history.SvnEditCommitMessageAction" popup="true"
icon="AllIcons.Actions.Edit"
text="Edit Revision Comment" description="Edit revision comment. Previous message is rewritten.">
<add-to-group group-id="CommittedChangesToolbar" anchor="last"/>
</action>
@@ -60,7 +62,8 @@
<separator/>
<action id="Subversion.ShowProperties" class="org.jetbrains.idea.svn.actions.ShowPropertiesAction"/>
<action id="Subversion.SetProperty" class="org.jetbrains.idea.svn.actions.SetPropertyAction"/>
<action id="ShowPropertiesDiffVsLocal" class="org.jetbrains.idea.svn.actions.ShowPropertiesDiffWithLocalAction" icon="SvnIcons.PropertiesDiffWithLocal">
<action id="ShowPropertiesDiffVsLocal" class="org.jetbrains.idea.svn.actions.ShowPropertiesDiffWithLocalAction"
icon="SvnIcons.PropertiesDiffWithLocal">
</action>
<reference id="ChangesView.Revert"/>
<action id="Subversion.Resolve" class="org.jetbrains.idea.svn.actions.ResolveAction"/>
@@ -86,7 +89,8 @@
<separator/>
<add-to-group group-id="SubversionFilePopupGroup" anchor="last"/>
</group>
<action id="Subversion.MarkLocallyDeletedTreeResolved" class="org.jetbrains.idea.svn.actions.MarkLocallyDeletedTreeConflictResolvedAction">
<action id="Subversion.MarkLocallyDeletedTreeResolved"
class="org.jetbrains.idea.svn.actions.MarkLocallyDeletedTreeConflictResolvedAction">
<add-to-group group-id="ChangesViewPopupMenu" anchor="after" relative-to-action="ChangesView.Diff"/>
</action>
<reference id="Subversion.MarkTreeResolved">
@@ -131,16 +135,24 @@
<projectService serviceInterface="org.jetbrains.idea.svn.branchConfig.SvnLoadedBranchesStorage"
serviceImplementation="org.jetbrains.idea.svn.branchConfig.SvnLoadedBranchesStorage"/>
<applicationService serviceInterface="org.jetbrains.idea.svn.branchConfig.SvnBranchMapperManager"
serviceImplementation="org.jetbrains.idea.svn.branchConfig.SvnBranchMapperManager"/>
serviceImplementation="org.jetbrains.idea.svn.branchConfig.SvnBranchMapperManager"/>
<applicationService serviceInterface="org.jetbrains.idea.svn.dialogs.browserCache.SvnRepositoryCache"
serviceImplementation="org.jetbrains.idea.svn.dialogs.browserCache.SvnRepositoryCache"/>
serviceImplementation="org.jetbrains.idea.svn.dialogs.browserCache.SvnRepositoryCache"/>
<applicationService serviceInterface="org.jetbrains.idea.svn.dialogs.browserCache.Loader"
serviceImplementation="org.jetbrains.idea.svn.dialogs.browserCache.CacheLoader"/>
serviceImplementation="org.jetbrains.idea.svn.dialogs.browserCache.CacheLoader"/>
<search.topHitProvider implementation="org.jetbrains.idea.svn.SvnOptionsTopHitProvider"/>
<vcs name="svn" vcsClass="org.jetbrains.idea.svn.SvnVcs" displayName="Subversion" administrativeAreaName=".svn, _svn" crawlUpToCheckUnderVcs="true"/>
<vcs name="svn" vcsClass="org.jetbrains.idea.svn.SvnVcs" displayName="Subversion" administrativeAreaName=".svn, _svn"
crawlUpToCheckUnderVcs="true"/>
<vcsPopupProvider implementation="org.jetbrains.idea.svn.actions.SvnQuickListContentProvider"/>
<statistics.usagesCollector implementation="org.jetbrains.idea.svn.statistics.SvnWorkingCopyFormatUsagesCollector"/>
<openapi.vcs.changes.actions.diff.ChangeDiffRequestProvider
implementation="org.jetbrains.idea.svn.difftool.SvnChangeDiffRequestProvider"/>
<openapi.util.diff.api.DiffTool implementation="org.jetbrains.idea.svn.difftool.properties.SvnPropertiesDiffTool"/>
<applicationService serviceInterface="org.jetbrains.idea.svn.difftool.SvnDiffSettingsHolder"
serviceImplementation="org.jetbrains.idea.svn.difftool.SvnDiffSettingsHolder"/>
</extensions>
</idea-plugin>
@@ -496,6 +496,7 @@ diff.options.title=Compare With Branch or Tag
diff.options.no.url.error=Please select an URL to compare with
diff.options.same.url.error=Please select different URLs to compare
diff.options.no.patch.file.error=Please specify the path to save the patch file
diff.cant.get.properties.changes=Can't show SVN properties
repository.browser.browse.changes.action=_Browse Changes...
repository.browser.browse.changes.description=View history of changes in the selected node
repository.browser.compare.title=Compare of ''{0} and ''{1}''
@@ -33,7 +33,9 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.actions.AbstractShowPropertiesDiffAction;
import org.jetbrains.idea.svn.api.NodeKind;
import org.jetbrains.idea.svn.branchConfig.SvnBranchConfigurationManager;
import org.jetbrains.idea.svn.history.SimplePropertyRevision;
import org.jetbrains.idea.svn.info.Info;
import org.jetbrains.idea.svn.properties.PropertyData;
import org.jetbrains.idea.svn.status.Status;
import org.jetbrains.idea.svn.status.StatusType;
import org.tmatesoft.svn.core.SVNException;
@@ -377,7 +379,7 @@ class SvnChangeProviderContext implements StatusReceiver {
ContentRevision contentRevision = isBeforeRevision ? change.getBeforeRevision() : change.getAfterRevision();
SVNRevision revision = isBeforeRevision ? SVNRevision.BASE : SVNRevision.WORKING;
return new SimpleContentRevision(getProperties(file, revision), path, getRevisionNumber(contentRevision));
return new SimplePropertyRevision(getProperties(file, revision), path, getRevisionNumber(contentRevision));
}
@Nullable
@@ -386,7 +388,7 @@ class SvnChangeProviderContext implements StatusReceiver {
}
@NotNull
private String getProperties(@NotNull File file, @NotNull SVNRevision revision) throws SVNException {
private List<PropertyData> getProperties(@NotNull File file, @NotNull SVNRevision revision) throws SVNException {
return AbstractShowPropertiesDiffAction.getPropertyList(myVcs, file, revision);
}
@@ -16,9 +16,6 @@
package org.jetbrains.idea.svn.actions;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.diff.DiffManager;
import com.intellij.openapi.diff.SimpleContent;
import com.intellij.openapi.diff.SimpleDiffRequest;
import com.intellij.openapi.progress.PerformInBackgroundOption;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
@@ -27,6 +24,8 @@ import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.diff.DiffDialogHints;
import com.intellij.openapi.util.diff.DiffManager;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.AbstractVcs;
import com.intellij.openapi.vcs.FilePath;
@@ -43,6 +42,8 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnBundle;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.api.Depth;
import org.jetbrains.idea.svn.difftool.properties.SvnPropertiesDiffRequest;
import org.jetbrains.idea.svn.difftool.properties.SvnPropertiesDiffRequest.PropertyContent;
import org.jetbrains.idea.svn.history.SvnRepositoryContentRevision;
import org.jetbrains.idea.svn.properties.PropertyConsumer;
import org.jetbrains.idea.svn.properties.PropertyData;
@@ -127,8 +128,8 @@ public abstract class AbstractShowPropertiesDiffAction extends AnAction implemen
private class CalculateAndShow extends Task.Backgroundable {
private final Change myChange;
private String myBeforeContent;
private String myAfterContent;
private List<PropertyData> myBeforeContent;
private List<PropertyData> myAfterContent;
private SVNRevision myBeforeRevisionValue;
private SVNRevision myAfterRevision;
private Exception myException;
@@ -167,16 +168,17 @@ public abstract class AbstractShowPropertiesDiffAction extends AnAction implemen
return;
}
if (myBeforeContent != null && myAfterContent != null && myBeforeRevisionValue != null && myAfterRevision != null) {
final SimpleDiffRequest diffRequest = new SimpleDiffRequest(myProject, getDiffWindowTitle(myChange));
SvnPropertiesDiffRequest diffRequest;
if (compareRevisions(myBeforeRevisionValue, myAfterRevision) > 0) {
// before ahead
diffRequest.setContents(new SimpleContent(myAfterContent), new SimpleContent(myBeforeContent));
diffRequest.setContentTitles(revisionToString(myAfterRevision), revisionToString(myBeforeRevisionValue));
diffRequest = new SvnPropertiesDiffRequest(getDiffWindowTitle(myChange),
new PropertyContent(myAfterContent), new PropertyContent(myBeforeContent),
revisionToString(myAfterRevision), revisionToString(myBeforeRevisionValue));
} else {
diffRequest.setContents(new SimpleContent(myBeforeContent), new SimpleContent(myAfterContent));
diffRequest.setContentTitles(revisionToString(myBeforeRevisionValue), revisionToString(myAfterRevision));
diffRequest = new SvnPropertiesDiffRequest(getDiffWindowTitle(myChange),
new PropertyContent(myBeforeContent), new PropertyContent(myAfterContent),
revisionToString(myBeforeRevisionValue), revisionToString(myAfterRevision));
}
DiffManager.getInstance().getDiffTool().show(diffRequest);
DiffManager.getInstance().showDiff(myProject, diffRequest, DiffDialogHints.DEFAULT);
}
}
});
@@ -225,12 +227,12 @@ public abstract class AbstractShowPropertiesDiffAction extends AnAction implemen
private final static String ourPropertiesDelimiter = "\n";
@NotNull
private static String getPropertyList(@NotNull SvnVcs vcs,
@Nullable final ContentRevision contentRevision,
@Nullable final SVNRevision revision)
private static List<PropertyData> getPropertyList(@NotNull SvnVcs vcs,
@Nullable final ContentRevision contentRevision,
@Nullable final SVNRevision revision)
throws SVNException, VcsException {
if (contentRevision == null) {
return "";
return null;
}
SvnTarget target;
@@ -246,13 +248,13 @@ public abstract class AbstractShowPropertiesDiffAction extends AnAction implemen
}
@NotNull
public static String getPropertyList(@NotNull SvnVcs vcs, @NotNull final SVNURL url, @Nullable final SVNRevision revision)
public static List<PropertyData> getPropertyList(@NotNull SvnVcs vcs, @NotNull final SVNURL url, @Nullable final SVNRevision revision)
throws VcsException {
return getPropertyList(vcs, SvnTarget.fromURL(url, revision), revision);
}
@NotNull
public static String getPropertyList(@NotNull SvnVcs vcs, @NotNull final File ioFile, @Nullable final SVNRevision revision)
public static List<PropertyData> getPropertyList(@NotNull SvnVcs vcs, @NotNull final File ioFile, @Nullable final SVNRevision revision)
throws SVNException {
try {
return getPropertyList(vcs, SvnTarget.fromFile(ioFile, revision), revision);
@@ -263,14 +265,14 @@ public abstract class AbstractShowPropertiesDiffAction extends AnAction implemen
}
@NotNull
private static String getPropertyList(@NotNull SvnVcs vcs, @NotNull SvnTarget target, @Nullable SVNRevision revision)
private static List<PropertyData> getPropertyList(@NotNull SvnVcs vcs, @NotNull SvnTarget target, @Nullable SVNRevision revision)
throws VcsException {
final List<PropertyData> lines = new ArrayList<PropertyData>();
final PropertyConsumer propertyHandler = createHandler(revision, lines);
vcs.getFactory(target).createPropertyClient().list(target, revision, Depth.EMPTY, propertyHandler);
return toSortedStringPresentation(lines);
return lines;
}
@NotNull
@@ -305,7 +307,7 @@ public abstract class AbstractShowPropertiesDiffAction extends AnAction implemen
}
@NotNull
private static String toSortedStringPresentation(@NotNull List<PropertyData> lines) {
public static String toSortedStringPresentation(@NotNull List<PropertyData> lines) {
StringBuilder sb = new StringBuilder();
Collections.sort(lines, new Comparator<PropertyData>() {
@@ -0,0 +1,126 @@
package org.jetbrains.idea.svn.difftool;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.UserDataHolder;
import com.intellij.openapi.util.diff.chains.DiffRequestPresentableException;
import com.intellij.openapi.util.diff.impl.DiffViewerWrapper;
import com.intellij.openapi.util.diff.requests.DiffRequest;
import com.intellij.openapi.util.diff.requests.ErrorDiffRequest;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ContentRevision;
import com.intellij.openapi.vcs.changes.actions.diff.ChangeDiffRequestPresentable;
import com.intellij.openapi.vcs.changes.actions.diff.ChangeDiffRequestProvider;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnBundle;
import org.jetbrains.idea.svn.SvnChangeProvider;
import org.jetbrains.idea.svn.difftool.properties.SvnPropertiesDiffRequest;
import org.jetbrains.idea.svn.history.PropertyRevision;
import org.jetbrains.idea.svn.properties.PropertyData;
import java.util.List;
import java.util.Map;
public class SvnChangeDiffRequestProvider implements ChangeDiffRequestProvider {
private static final Logger LOG = Logger.getInstance(SvnChangeDiffRequestProvider.class);
@Override
public boolean canCreate(@NotNull Project project, @NotNull Change change) {
return getSvnChangeLayer(change) != null; // TODO: do not show, if no properties are set in both revisions ?
}
@NotNull
@Override
public DiffRequest process(@NotNull ChangeDiffRequestPresentable presentable,
@NotNull UserDataHolder context,
@NotNull ProgressIndicator indicator) throws DiffRequestPresentableException, ProcessCanceledException {
DiffRequestPresentableException e1 = null;
DiffRequestPresentableException e2 = null;
DiffRequest propertyRequest;
try {
propertyRequest = createPropertyRequest(presentable.getChange(), indicator);
}
catch (DiffRequestPresentableException e) {
e1 = e;
propertyRequest = new ErrorDiffRequest(presentable, e);
}
DiffRequest contentRequest;
try {
contentRequest = ChangeDiffRequestPresentable.createRequest(presentable.getProject(), presentable.getChange(), context, indicator);
}
catch (DiffRequestPresentableException e) {
e2 = e;
contentRequest = new ErrorDiffRequest(presentable, e);
}
if (e1 != null && e2 != null) {
LOG.info(e1);
LOG.info(e2);
throw new DiffRequestPresentableException(e1.getMessage() + "\n\n" + e2.getMessage());
}
contentRequest.putUserData(DiffViewerWrapper.KEY, new SvnDiffViewerWrapper(propertyRequest));
return contentRequest;
}
@NotNull
private static SvnPropertiesDiffRequest createPropertyRequest(@NotNull Change change, @NotNull ProgressIndicator indicator)
throws DiffRequestPresentableException {
try {
Change propertiesChange = getSvnChangeLayer(change);
if (propertiesChange == null) throw new DiffRequestPresentableException(SvnBundle.getString("diff.cant.get.properties.changes"));
ContentRevision bRevRaw = propertiesChange.getBeforeRevision();
ContentRevision aRevRaw = propertiesChange.getAfterRevision();
if (bRevRaw != null && !(bRevRaw instanceof PropertyRevision)) {
LOG.warn("Before change is not PropertyRevision");
throw new DiffRequestPresentableException(SvnBundle.getString("diff.cant.get.properties.changes"));
}
if (aRevRaw != null && !(aRevRaw instanceof PropertyRevision)) {
LOG.warn("After change is not PropertyRevision");
throw new DiffRequestPresentableException(SvnBundle.getString("diff.cant.get.properties.changes"));
}
PropertyRevision bRev = (PropertyRevision)bRevRaw;
PropertyRevision aRev = (PropertyRevision)aRevRaw;
indicator.checkCanceled();
List<PropertyData> bContent = bRev != null ? bRev.getProperties() : null;
indicator.checkCanceled();
List<PropertyData> aContent = aRev != null ? aRev.getProperties() : null;
if (aRev == null && bRev == null) throw new DiffRequestPresentableException(SvnBundle.getString("diff.cant.get.properties.changes"));
ContentRevision bRevMain = change.getBeforeRevision();
ContentRevision aRevMain = change.getAfterRevision();
String title1 = bRevMain != null ? StringUtil.nullize(bRevMain.getRevisionNumber().asString()) : null;
String title2 = aRevMain != null ? StringUtil.nullize(aRevMain.getRevisionNumber().asString()) : null;
return new SvnPropertiesDiffRequest(bContent, aContent, title1, title2);
}
catch (VcsException e) {
throw new DiffRequestPresentableException(e);
}
}
@Nullable
private static Change getSvnChangeLayer(@NotNull Change change) {
for (Map.Entry<String, Change> entry : change.getOtherLayers().entrySet()) {
if (SvnChangeProvider.PROPERTY_LAYER.equals(entry.getKey())) {
if (change.getOtherLayers().size() != 1) LOG.warn("Some of change layers ignored");
return entry.getValue();
}
}
return null;
}
}
@@ -0,0 +1,80 @@
package org.jetbrains.idea.svn.difftool;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.diff.util.DiffUtil;
import org.jetbrains.annotations.NotNull;
@State(
name = "SvnDiffSettings",
storages = {@Storage(
file = DiffUtil.DIFF_CONFIG)})
public class SvnDiffSettingsHolder implements PersistentStateComponent<SvnDiffSettingsHolder.SvnDiffSettings> {
public static class SvnDiffSettings {
public static final Key<SvnDiffSettings> KEY = Key.create("SvnDiffSettings");
float SPITTER_PROPORTION = 0.9f;
boolean HIDE_PROPERTIES = false;
public SvnDiffSettings() {
}
public SvnDiffSettings(float SPITTER_PROPORTION, boolean HIDE_PROPERTIES) {
this.SPITTER_PROPORTION = SPITTER_PROPORTION;
this.HIDE_PROPERTIES = HIDE_PROPERTIES;
}
@NotNull
private SvnDiffSettings copy() {
return new SvnDiffSettings(SPITTER_PROPORTION, HIDE_PROPERTIES);
}
public boolean isHideProperties() {
return HIDE_PROPERTIES;
}
public void setHideProperties(boolean value) {
HIDE_PROPERTIES = value;
}
public float getSplitterProportion() {
return SPITTER_PROPORTION;
}
public void setSplitterProportion(float value) {
SPITTER_PROPORTION = value;
}
//
// Impl
//
@NotNull
public static SvnDiffSettings getSettings() {
return getInstance().getState().copy();
}
@NotNull
public static SvnDiffSettings getSettingsDefaults() {
return getInstance().getState();
}
}
private SvnDiffSettings myState = new SvnDiffSettings();
@NotNull
public SvnDiffSettings getState() {
return myState;
}
public void loadState(SvnDiffSettings state) {
myState = state;
}
public static SvnDiffSettingsHolder getInstance() {
return ServiceManager.getService(SvnDiffSettingsHolder.class);
}
}
@@ -0,0 +1,335 @@
package org.jetbrains.idea.svn.difftool;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.ToggleAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Splitter;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.openapi.util.diff.api.FrameDiffTool.DiffContext;
import com.intellij.openapi.util.diff.api.FrameDiffTool.DiffViewer;
import com.intellij.openapi.util.diff.api.FrameDiffTool.ToolbarComponents;
import com.intellij.openapi.util.diff.contents.DiffContent;
import com.intellij.openapi.util.diff.contents.EmptyContent;
import com.intellij.openapi.util.diff.impl.ModifiablePanel;
import com.intellij.openapi.util.diff.requests.DiffRequest;
import com.intellij.openapi.util.diff.requests.ErrorDiffRequest;
import com.intellij.openapi.util.diff.tools.ErrorDiffTool;
import com.intellij.openapi.util.diff.util.DiffUtil;
import com.intellij.ui.EditorNotificationPanel;
import com.intellij.ui.OnePixelSplitter;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.difftool.SvnDiffSettingsHolder.SvnDiffSettings;
import org.jetbrains.idea.svn.difftool.properties.SvnPropertiesDiffRequest;
import org.jetbrains.idea.svn.difftool.properties.SvnPropertiesDiffViewer;
import org.jetbrains.idea.svn.properties.PropertyData;
import org.jetbrains.idea.svn.properties.PropertyValue;
import javax.swing.*;
import java.awt.*;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class SvnDiffViewer implements DiffViewer {
private static final Logger LOG = Logger.getInstance(SvnDiffViewer.class);
public static final Key<MyPropertyContext> PROPERTY_CONTEXT_KEY = Key.create("MyPropertyContext");
public static final Key<Boolean> FOCUSED_VIEWER_KEY = Key.create("SvnFocusedViewer");
@Nullable private final Project myProject;
@NotNull private final DiffContext myContext;
@NotNull private final DiffRequest myPropertyRequest;
@NotNull private final SvnDiffSettings mySettings;
@NotNull private final JPanel myPanel;
@NotNull private final Splitter mySplitter;
@NotNull private final ModifiablePanel myNotificationPanel;
@NotNull private final DiffViewer myContentViewer;
@NotNull private final DiffViewer myPropertiesViewer;
@NotNull private final FocusListener myContentFocusListener = new MyFocusListener(false);
@NotNull private final FocusListener myPropertiesFocusListener = new MyFocusListener(true);
private boolean myPropertiesViewerFocused; // False - content viewer, True - properties
private boolean myDumbContentViewer;
public SvnDiffViewer(@NotNull DiffContext context, @NotNull DiffRequest propertyRequest, @NotNull DiffViewer wrappingViewer) {
myProject = context.getProject();
myContext = context;
myPropertyRequest = propertyRequest;
myContentViewer = wrappingViewer;
myPropertyRequest.onAssigned(true);
mySettings = initSettings(context);
mySplitter = new OnePixelSplitter(true);
mySplitter.setProportion(mySettings.getSplitterProportion());
mySplitter.setFirstComponent(myContentViewer.getComponent());
myNotificationPanel = new ModifiablePanel();
MyPropertyContext propertyContext = initPropertyContext(context);
myPropertiesViewer = createPropertiesViewer(propertyRequest, propertyContext);
myPanel = new JPanel(new BorderLayout());
myPanel.add(mySplitter, BorderLayout.CENTER);
myPanel.add(myNotificationPanel, BorderLayout.SOUTH);
updatePropertiesPanel();
}
@NotNull
private static DiffViewer createPropertiesViewer(@NotNull DiffRequest propertyRequest, @NotNull MyPropertyContext propertyContext) {
if (propertyRequest instanceof SvnPropertiesDiffRequest) {
return new SvnPropertiesDiffViewer(propertyContext, ((SvnPropertiesDiffRequest)propertyRequest));
}
else {
return ErrorDiffTool.INSTANCE.createComponent(propertyContext, propertyRequest);
}
}
@NotNull
@Override
public ToolbarComponents init() {
installListeners();
processContextHints();
ToolbarComponents properties = myPropertiesViewer.init();
ToolbarComponents components = new ToolbarComponents();
components.toolbarActions = createToolbar(properties.toolbarActions);
return components;
}
@Override
public void dispose() {
destroyListeners();
updateContextHints();
Disposer.dispose(myPropertiesViewer);
myPropertyRequest.onAssigned(false);
}
private void processContextHints() {
if (myContext.getUserData(FOCUSED_VIEWER_KEY) == Boolean.TRUE) myPropertiesViewerFocused = true;
myDumbContentViewer = myContentViewer.getPreferredFocusedComponent() == null;
}
private void updateContextHints() {
if (!myDumbContentViewer) myContext.putUserData(FOCUSED_VIEWER_KEY, myPropertiesViewerFocused);
mySettings.setSplitterProportion(mySplitter.getProportion());
}
//
// Diff
//
@NotNull
private JComponent createNotification() {
if (myPropertyRequest instanceof ErrorDiffRequest) {
return createNotification(((ErrorDiffRequest)myPropertyRequest).getErrorMessage());
}
DiffContent[] contents = ((SvnPropertiesDiffRequest)myPropertyRequest).getContents();
Map<String, PropertyValue> before = getProperties(contents[0]);
Map<String, PropertyValue> after = getProperties(contents[1]);
if (before.isEmpty() && after.isEmpty()) return createNotification("No SVN properties set");
if (!before.keySet().equals(after.keySet())) {
return createNotification("SVN Properties changed");
}
for (String key : before.keySet()) {
if (!Comparing.equal(before.get(key), after.get(key))) return createNotification("SVN Properties changed");
}
return createNotification("SVN Properties unchanged");
}
@NotNull
private static Map<String, PropertyValue> getProperties(@NotNull DiffContent content) {
if (content instanceof EmptyContent) return Collections.emptyMap();
List<PropertyData> properties = ((SvnPropertiesDiffRequest.PropertyContent)content).getProperties();
Map<String, PropertyValue> map = new HashMap<String, PropertyValue>();
for (PropertyData data : properties) {
if (map.containsKey(data.getName())) LOG.warn("Duplicated property: " + data.getName());
map.put(data.getName(), data.getValue());
}
return map;
}
@NotNull
private static JPanel createNotification(@NotNull String text) {
return new EditorNotificationPanel().text(text);
}
//
// Misc
//
private void updatePropertiesPanel() {
if (!mySettings.isHideProperties()) {
mySplitter.setSecondComponent(myPropertiesViewer.getComponent());
myNotificationPanel.setContent(null);
}
else {
mySplitter.setSecondComponent(null);
myNotificationPanel.setContent(createNotification());
}
}
@NotNull
private List<AnAction> createToolbar(@Nullable List<AnAction> propertiesActions) {
List<AnAction> result = new ArrayList<AnAction>();
if (propertiesActions != null) result.addAll(propertiesActions);
result.add(new ToggleHidePropertiesAction());
return result;
}
@NotNull
private static SvnDiffSettings initSettings(@NotNull DiffContext context) {
SvnDiffSettings settings = context.getUserData(SvnDiffSettings.KEY);
if (settings == null) {
settings = SvnDiffSettings.getSettingsDefaults(); // TODO: do not use defaults ?
context.putUserData(SvnDiffSettings.KEY, settings);
}
return settings;
}
@NotNull
private MyPropertyContext initPropertyContext(@NotNull DiffContext context) {
MyPropertyContext propertyContext = context.getUserData(PROPERTY_CONTEXT_KEY);
if (propertyContext == null) {
propertyContext = new MyPropertyContext();
context.putUserData(PROPERTY_CONTEXT_KEY, propertyContext);
}
return propertyContext;
}
private void installListeners() {
myContentViewer.getComponent().addFocusListener(myContentFocusListener);
myPropertiesViewer.getComponent().addFocusListener(myPropertiesFocusListener);
}
private void destroyListeners() {
myContentViewer.getComponent().removeFocusListener(myContentFocusListener);
myPropertiesViewer.getComponent().removeFocusListener(myPropertiesFocusListener);
}
//
// Getters
//
@NotNull
@Override
public JComponent getComponent() {
return myPanel;
}
@Nullable
@Override
public JComponent getPreferredFocusedComponent() {
if (myPropertiesViewerFocused) {
JComponent component = myPropertiesViewer.getPreferredFocusedComponent();
if (component != null) return component;
return myContentViewer.getPreferredFocusedComponent();
}
else {
JComponent component = myContentViewer.getPreferredFocusedComponent();
if (component != null) return component;
return myPropertiesViewer.getPreferredFocusedComponent();
}
}
//
// Actions
//
private class ToggleHidePropertiesAction extends ToggleAction implements DumbAware { // TODO: shortcut
public ToggleHidePropertiesAction() {
super("Show SVN properties", null, AllIcons.Actions.Preview);
}
@Override
public boolean isSelected(AnActionEvent e) {
return !mySettings.isHideProperties();
}
@Override
public void setSelected(AnActionEvent e, boolean state) {
mySettings.setHideProperties(!state);
updatePropertiesPanel();
}
}
//
// Helpers
//
private class MyPropertyContext extends UserDataHolderBase implements DiffContext {
private final DiffWindow myWindow = new DiffWindow() {
@Override
public boolean isFocused() {
return DiffUtil.isFocusedComponent(getProject(), myPropertiesViewer.getComponent());
}
@Override
public void requestFocus() {
DiffUtil.requestFocus(getProject(), myPropertiesViewer.getPreferredFocusedComponent());
}
};
@Nullable
@Override
public Project getProject() {
return myContext.getProject();
}
@NotNull
@Override
public DiffWindow getDiffWindow() {
return myWindow;
}
}
private class MyFocusListener extends FocusAdapter {
private final boolean myValue;
public MyFocusListener(boolean value) {
myValue = value;
}
@Override
public void focusGained(FocusEvent e) {
myPropertiesViewerFocused = myValue;
}
}
}
@@ -0,0 +1,20 @@
package org.jetbrains.idea.svn.difftool;
import com.intellij.openapi.util.diff.api.FrameDiffTool.DiffContext;
import com.intellij.openapi.util.diff.api.FrameDiffTool.DiffViewer;
import com.intellij.openapi.util.diff.impl.DiffViewerWrapper;
import com.intellij.openapi.util.diff.requests.DiffRequest;
import org.jetbrains.annotations.NotNull;
public class SvnDiffViewerWrapper implements DiffViewerWrapper {
@NotNull private final DiffRequest myPropertyRequest;
public SvnDiffViewerWrapper(@NotNull DiffRequest propertyRequest) {
myPropertyRequest = propertyRequest;
}
@Override
public DiffViewer createComponent(@NotNull DiffContext context, @NotNull DiffRequest request, @NotNull DiffViewer wrappedViewer) {
return new SvnDiffViewer(context, myPropertyRequest, wrappedViewer);
}
}
@@ -0,0 +1,254 @@
package org.jetbrains.idea.svn.difftool.properties;
import com.intellij.openapi.util.diff.util.Side;
import com.intellij.openapi.util.diff.util.TextDiffType;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.JBColor;
import com.intellij.util.ui.ColumnInfo;
import com.intellij.util.ui.ListTableModel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.difftool.properties.SvnPropertiesDiffViewer.PropertyDiffRecord;
import org.jetbrains.idea.svn.difftool.properties.SvnPropertiesDiffViewer.PropertyDiffRecord.ColoredChunk;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import java.awt.*;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
// FIXME: colors on selection
public class PropertiesTableModel extends ListTableModel<PropertyDiffRecord> {
@NotNull private final MultiLineTableRenderer myRenderer = new MultiLineTableRenderer();
public static int NAME_COL = 0;
public PropertiesTableModel(boolean showLeft, boolean showRight, @Nullable String title1, @Nullable String title2) {
if (showLeft && showRight) {
setColumnInfos(new ColumnInfo[]{
new NameColumnInfo(myRenderer),
new ValueColumnInfo(Side.LEFT, title1, myRenderer),
new ValueColumnInfo(Side.RIGHT, title2, myRenderer)});
}
else if (showLeft) {
setColumnInfos(new ColumnInfo[]{
new NameColumnInfo(myRenderer),
new ValueColumnInfo(Side.LEFT, title1, myRenderer)});
}
else if (showRight) {
setColumnInfos(new ColumnInfo[]{
new NameColumnInfo(myRenderer),
new ValueColumnInfo(Side.RIGHT, title2, myRenderer)});
}
else {
throw new IllegalStateException();
}
}
@Override
public RowSorter.SortKey getDefaultSortKey() {
return new RowSorter.SortKey(NAME_COL, SortOrder.DESCENDING);
}
private static TextDiffType getDiffType(PropertyDiffRecord record) {
if (record.isChanged()) {
if (record.getBefore() == null) {
return TextDiffType.INSERTED;
}
else if (record.getAfter() == null) {
return TextDiffType.DELETED;
}
else {
return TextDiffType.MODIFIED;
}
}
return null;
}
private static String getValueTitle(@NotNull Side side, @Nullable String title) {
if (title != null) return "Value in " + title;
return side.isLeft() ? "Value Before" : "Value After";
}
private static class NameColumnInfo extends ColumnInfo<PropertyDiffRecord, String> {
@NotNull private final MultiLineTableRenderer myRenderer;
public NameColumnInfo(@NotNull MultiLineTableRenderer renderer) {
super("SVN Property Name");
myRenderer = renderer;
}
@Nullable
@Override
public TableCellRenderer getRenderer(final PropertyDiffRecord record) {
TextDiffType type = getDiffType(record);
final Color bgColor = type != null ? type.getIgnoredColor(null) : null;
myRenderer.setCustomizer(new MultiLineTableRenderer.Customizer() {
@Override
public void customize(JTable table, JLabel label, boolean isSelected, Object value) {
label.setText(record.getName());
if (bgColor != null) {
label.setForeground(table.getForeground());
label.setBackground(bgColor);
}
}
});
return myRenderer;
}
@Nullable
@Override
public String valueOf(PropertyDiffRecord data) {
return data.getName();
}
@Nullable
@Override
public String getPreferredStringValue() {
return "svn:some_property_name";
}
@Nullable
@Override
public String getMaxStringValue() {
return "svn:some_really_big_cool_property_name";
}
@Nullable
@Override
public Comparator<PropertyDiffRecord> getComparator() {
return new Comparator<PropertyDiffRecord>() {
@Override
public int compare(PropertyDiffRecord o1, PropertyDiffRecord o2) {
return StringUtil.naturalCompare(o1.getName(), o2.getName());
}
};
}
}
private static class ValueColumnInfo extends ColumnInfo<PropertyDiffRecord, String> {
@NotNull private final MultiLineTableRenderer myRenderer;
@NotNull private final Side mySide;
public ValueColumnInfo(@NotNull Side side, @Nullable String title, @NotNull MultiLineTableRenderer renderer) {
super(getValueTitle(side, title));
mySide = side;
myRenderer = renderer;
}
@Nullable
private List<ColoredChunk> getChunks(@NotNull PropertyDiffRecord record) {
return mySide.isLeft() ? record.getBefore() : record.getAfter();
}
@Nullable
@Override
public TableCellRenderer getRenderer(final PropertyDiffRecord record) {
TextDiffType type = getDiffType(record);
final Color bgColor = type != null ? type.getIgnoredColor(null) : null;
myRenderer.setCustomizer(new MultiLineTableRenderer.Customizer() {
@Override
public void customize(JTable table, JLabel label, boolean isSelected, Object value) {
List<ColoredChunk> chunks = getChunks(record);
if (chunks == null) {
label.setText("");
label.setBackground(JBColor.border()); // TODO: better color
}
else {
StringBuilder text = new StringBuilder();
text.append("<html><body style=\"white-space:nowrap\">");
for (ColoredChunk chunk : chunks) {
text.append("<span");
if (chunk.getType() != null) {
text.append(" style=\"");
text.append("background-color:#").append(Integer.toString(chunk.getType().getColor(null).getRGB() & 0xFFFFFF, 16));
text.append("\"");
}
text.append('>');
text.append(StringUtil.escapeXml(chunk.getText()).replaceAll("\n", "<br>"));
text.append("</span>");
}
text.append("</body></html>");
label.setText(text.toString());
if (bgColor != null) {
label.setForeground(table.getForeground());
label.setBackground(bgColor);
}
}
}
});
return myRenderer;
}
@Nullable
@Override
public String valueOf(@NotNull PropertyDiffRecord record) {
List<ColoredChunk> chunks = getChunks(record);
if (chunks == null) return null;
StringBuilder builder = new StringBuilder();
for (ColoredChunk chunk : chunks) {
builder.append(chunk.getText());
}
return builder.toString();
}
}
private static class MultiLineTableRenderer extends DefaultTableCellRenderer {
@NotNull private final List<List<Integer>> rowColHeight = new ArrayList<List<Integer>>();
@Nullable private Customizer myCustomizer;
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
setBackground(null);
final JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (myCustomizer != null) myCustomizer.customize(table, label, isSelected, value);
adjustRowHeight(table, row, column);
return label;
}
/**
* Calculate the new preferred height for a given row, and sets the height on the table.
*/
private void adjustRowHeight(JTable table, int row, int column) {
//The trick to get this to work properly is to set the width of the column to the
//textarea. The reason for this is that getPreferredSize(), without a width tries
//to place all the text in one line. By setting the size with the with of the column,
//getPreferredSize() returnes the proper height which the row should have in
//order to make room for the text.
int prefH = getPreferredSize().height;
while (rowColHeight.size() <= row) {
rowColHeight.add(new ArrayList<Integer>(column));
}
List<Integer> colHeights = rowColHeight.get(row);
while (colHeights.size() <= column) {
colHeights.add(0);
}
colHeights.set(column, prefH);
int maxH = prefH;
for (Integer colHeight : colHeights) {
if (colHeight > maxH) {
maxH = colHeight;
}
}
if (table.getRowHeight(row) != maxH) {
table.setRowHeight(row, maxH);
}
}
public void setCustomizer(@Nullable Customizer customizer) {
myCustomizer = customizer;
}
public interface Customizer {
void customize(JTable table, JLabel textArea, boolean isSelected, Object value);
}
}
}
@@ -0,0 +1,100 @@
package org.jetbrains.idea.svn.difftool.properties;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.openapi.util.diff.contents.DiffContent;
import com.intellij.openapi.util.diff.contents.EmptyContent;
import com.intellij.openapi.util.diff.requests.ContentDiffRequest;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.properties.PropertyData;
import java.util.List;
public class SvnPropertiesDiffRequest extends UserDataHolderBase implements ContentDiffRequest {
@NotNull private final DiffContent[] myContents;
@NotNull private final String[] myContentTitles;
@NotNull private final String myWindowTitle;
public SvnPropertiesDiffRequest(@NotNull String windowTitle,
@NotNull DiffContent content1,
@NotNull DiffContent content2,
@NotNull String title1,
@NotNull String title2) {
myWindowTitle = windowTitle;
myContents = new DiffContent[]{content1, content2};
myContentTitles = new String[]{title1, title2};
assert content1 instanceof PropertyContent || content1 instanceof EmptyContent;
assert content2 instanceof PropertyContent || content2 instanceof EmptyContent;
assert content1 instanceof PropertyContent || content2 instanceof PropertyContent;
}
public SvnPropertiesDiffRequest(@Nullable List<PropertyData> before, @Nullable List<PropertyData> after,
@Nullable String title1, @Nullable String title2) {
assert before != null || after != null;
myContents = new DiffContent[]{createContent(before), createContent(after)};
myWindowTitle = "Svn Properties Diff";
myContentTitles = new String[]{title1, title2};
}
@NotNull
public DiffContent createContent(@Nullable List<PropertyData> content) {
if (content == null) return new EmptyContent();
return new PropertyContent(content);
}
@NotNull
@Override
public String getWindowTitle() {
return myWindowTitle;
}
@NotNull
@Override
public String[] getContentTitles() {
return myContentTitles;
}
@NotNull
@Override
public DiffContent[] getContents() {
return myContents;
}
@Override
public void onAssigned(boolean isAssigned) {
}
public static class PropertyContent implements DiffContent {
@NotNull private final List<PropertyData> myProperties;
public PropertyContent(@NotNull List<PropertyData> properties) {
myProperties = properties;
}
@NotNull
public List<PropertyData> getProperties() {
return myProperties;
}
@Nullable
@Override
public FileType getContentType() {
return null;
}
@Nullable
@Override
public OpenFileDescriptor getOpenFileDescriptor() {
return null;
}
@Override
public void onAssigned(boolean isAssigned) {
}
}
}
@@ -0,0 +1,24 @@
package org.jetbrains.idea.svn.difftool.properties;
import com.intellij.openapi.util.diff.api.FrameDiffTool;
import com.intellij.openapi.util.diff.requests.DiffRequest;
import org.jetbrains.annotations.NotNull;
public class SvnPropertiesDiffTool implements FrameDiffTool {
@NotNull
@Override
public String getName() {
return "Svn Properties Viewer";
}
@Override
public boolean canShow(@NotNull DiffContext context, @NotNull DiffRequest request) {
return request instanceof SvnPropertiesDiffRequest;
}
@NotNull
@Override
public DiffViewer createComponent(@NotNull DiffContext context, @NotNull DiffRequest request) {
return new SvnPropertiesDiffViewer(context, (SvnPropertiesDiffRequest)request);
}
}
@@ -0,0 +1,237 @@
package org.jetbrains.idea.svn.difftool.properties;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.diff.api.FrameDiffTool.DiffContext;
import com.intellij.openapi.util.diff.api.FrameDiffTool.DiffViewer;
import com.intellij.openapi.util.diff.api.FrameDiffTool.ToolbarComponents;
import com.intellij.openapi.util.diff.comparison.iterables.DiffIterable;
import com.intellij.openapi.util.diff.comparison.iterables.DiffIterableUtil;
import com.intellij.openapi.util.diff.contents.DiffContent;
import com.intellij.openapi.util.diff.fragments.DiffFragment;
import com.intellij.openapi.util.diff.util.DiffUtil;
import com.intellij.openapi.util.diff.util.TextDiffType;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.table.TableView;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.difftool.properties.SvnPropertiesDiffViewer.PropertyDiffRecord.ColoredChunk;
import org.jetbrains.idea.svn.properties.PropertyData;
import org.jetbrains.idea.svn.properties.PropertyValue;
import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.util.List;
public class SvnPropertiesDiffViewer implements DiffViewer {
private static final Logger LOG = Logger.getInstance(SvnPropertiesDiffViewer.class);
@Nullable private final Project myProject;
@NotNull private final DiffContext myContext;
@NotNull private final SvnPropertiesDiffRequest myRequest;
@Nullable private final List<PropertyData> myProperties1;
@Nullable private final List<PropertyData> myProperties2;
@NotNull private final JPanel myPanel;
@NotNull private final TableView<PropertyDiffRecord> myTable;
public SvnPropertiesDiffViewer(@NotNull DiffContext context, @NotNull SvnPropertiesDiffRequest request) {
myProject = context.getProject();
myContext = context;
myRequest = request;
String[] titles = request.getContentTitles();
DiffContent[] contents = request.getContents();
myProperties1 = getProperties(contents[0]);
myProperties2 = getProperties(contents[1]);
assert myProperties1 != null || myProperties2 != null;
PropertiesTableModel model = new PropertiesTableModel(myProperties1 != null, myProperties2 != null, titles[0], titles[1]);
myTable = new TableView<PropertyDiffRecord>(model);
myTable.getTableHeader().setReorderingAllowed(false);
myPanel = new JPanel(new BorderLayout());
myPanel.add(ScrollPaneFactory.createScrollPane(myTable), BorderLayout.CENTER);
}
@NotNull
@Override
public ToolbarComponents init() {
rediff();
ToolbarComponents components = new ToolbarComponents();
components.toolbarActions = createToolbar();
return components;
}
@Override
public void dispose() {
}
//
// Diff
//
public void rediff() {
assert myProperties1 != null || myProperties2 != null;
Map<String, PropertyValue> before = new HashMap<String, PropertyValue>();
Map<String, PropertyValue> after = new HashMap<String, PropertyValue>();
if (myProperties1 != null) {
for (PropertyData data : myProperties1) {
before.put(data.getName(), data.getValue());
}
}
if (myProperties2 != null) {
for (PropertyData data : myProperties2) {
after.put(data.getName(), data.getValue());
}
}
List<PropertyDiffRecord> records = new ArrayList<PropertyDiffRecord>();
for (String name : ContainerUtil.union(before.keySet(), after.keySet())) {
records.add(createRecord(name, before.get(name), after.get(name)));
}
ContainerUtil.sort(records, new Comparator<PropertyDiffRecord>() {
@Override
public int compare(PropertyDiffRecord o1, PropertyDiffRecord o2) {
return StringUtil.naturalCompare(o1.getName(), o2.getName());
}
});
((PropertiesTableModel)myTable.getModel()).setItems(records);
}
@Nullable
private static PropertyDiffRecord createRecord(@NotNull String name, @Nullable PropertyValue value1, @Nullable PropertyValue value2) {
assert value1 != null || value2 != null;
if (value1 == null) {
return new PropertyDiffRecord(name, null, Collections.singletonList(new ColoredChunk(value2.toString(), null)), true);
}
if (value2 == null) {
return new PropertyDiffRecord(name, Collections.singletonList(new ColoredChunk(value1.toString(), null)), null, true);
}
String text1 = value1.toString();
String text2 = value2.toString();
List<DiffFragment> fragments = DiffUtil.compareWords(text1, text2, 1000);
List<ColoredChunk> chunks1 = new ArrayList<ColoredChunk>();
List<ColoredChunk> chunks2 = new ArrayList<ColoredChunk>();
DiffIterable iterable = DiffIterableUtil.createFragments(fragments, text1.length(), text2.length());
for (Pair<DiffIterableUtil.Range, Boolean> pair : DiffIterableUtil.iterateAll(iterable)) {
DiffIterableUtil.Range range = pair.first;
Boolean equals = pair.second;
if (range.start1 == range.end1) {
chunks2.add(new ColoredChunk(text2.substring(range.start2, range.end2), equals ? null : TextDiffType.INSERTED));
}
else if (range.start2 == range.end2) {
chunks1.add(new ColoredChunk(text1.substring(range.start1, range.end1), equals ? null : TextDiffType.DELETED));
}
else {
chunks1.add(new ColoredChunk(text1.substring(range.start1, range.end1), equals ? null : TextDiffType.MODIFIED));
chunks2.add(new ColoredChunk(text2.substring(range.start2, range.end2), equals ? null : TextDiffType.MODIFIED));
}
}
return new PropertyDiffRecord(name, chunks1, chunks2, !fragments.isEmpty());
}
@Nullable
private static List<PropertyData> getProperties(@NotNull DiffContent content) {
if (content instanceof SvnPropertiesDiffRequest.PropertyContent) {
return ((SvnPropertiesDiffRequest.PropertyContent)content).getProperties();
}
return null;
}
@NotNull
private List<AnAction> createToolbar() {
List<AnAction> result = new ArrayList<AnAction>();
return result;
}
//
// Getters
//
@NotNull
@Override
public JComponent getComponent() {
return myPanel;
}
@Nullable
@Override
public JComponent getPreferredFocusedComponent() {
return myTable;
}
public static class PropertyDiffRecord {
@NotNull private final String myName;
@Nullable private final List<ColoredChunk> myBefore;
@Nullable private final List<ColoredChunk> myAfter;
private final boolean myIsChanged;
public PropertyDiffRecord(@NotNull String name,
@Nullable List<ColoredChunk> before,
@Nullable List<ColoredChunk> after,
boolean isChanged) {
myName = name;
myBefore = before;
myAfter = after;
myIsChanged = isChanged;
}
@NotNull
public String getName() {
return myName;
}
@Nullable
public List<ColoredChunk> getBefore() {
return myBefore;
}
@Nullable
public List<ColoredChunk> getAfter() {
return myAfter;
}
public boolean isChanged() {
return myIsChanged;
}
public static class ColoredChunk {
@NotNull private final String myText;
@Nullable private final TextDiffType myType;
public ColoredChunk(@NotNull String text, @Nullable TextDiffType type) {
myText = text;
myType = type;
}
@NotNull
public String getText() {
return myText;
}
@Nullable
public TextDiffType getType() {
return myType;
}
}
}
}
@@ -0,0 +1,13 @@
package org.jetbrains.idea.svn.history;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.ContentRevision;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.properties.PropertyData;
import java.util.List;
public interface PropertyRevision extends ContentRevision {
@Nullable
List<PropertyData> getProperties() throws VcsException;
}
@@ -0,0 +1,53 @@
package org.jetbrains.idea.svn.history;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.ContentRevision;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.actions.AbstractShowPropertiesDiffAction;
import org.jetbrains.idea.svn.properties.PropertyData;
import java.util.List;
public class SimplePropertyRevision implements ContentRevision, PropertyRevision {
private final List<PropertyData> myProperty;
private final FilePath myNewFilePath;
private final String myRevision;
@Nullable
@Override
public List<PropertyData> getProperties() throws VcsException {
return myProperty;
}
public SimplePropertyRevision(final List<PropertyData> property, final FilePath newFilePath, final String revision) {
myProperty = property;
myNewFilePath = newFilePath;
myRevision = revision;
}
@Nullable
public String getContent() {
return AbstractShowPropertiesDiffAction.toSortedStringPresentation(myProperty);
}
@NotNull
public FilePath getFile() {
return myNewFilePath;
}
@NotNull
public VcsRevisionNumber getRevisionNumber() {
return new VcsRevisionNumber() {
public String asString() {
return myRevision;
}
public int compareTo(final VcsRevisionNumber o) {
return 0;
}
};
}
}
@@ -26,24 +26,28 @@ import com.intellij.openapi.vcs.changes.ContentRevision;
import com.intellij.openapi.vcs.changes.MarkerVcsContentRevision;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnBundle;
import org.jetbrains.idea.svn.SvnRevisionNumber;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.actions.AbstractShowPropertiesDiffAction;
import org.jetbrains.idea.svn.properties.PropertyData;
import org.tmatesoft.svn.core.SVNURL;
import java.util.List;
/**
* Created with IntelliJ IDEA.
* User: Irina.Chernushina
* Date: 2/22/12
* Time: 10:28 AM
*/
public class SvnLazyPropertyContentRevision implements ContentRevision, MarkerVcsContentRevision {
public class SvnLazyPropertyContentRevision implements ContentRevision, MarkerVcsContentRevision, PropertyRevision {
private final FilePath myPath;
private final VcsRevisionNumber myNumber;
private final Project myProject;
private final SVNURL myUrl;
private String myContent;
private List<PropertyData> myContent;
public SvnLazyPropertyContentRevision(FilePath path, VcsRevisionNumber number, Project project, SVNURL url) {
myPath = path;
@@ -52,17 +56,24 @@ public class SvnLazyPropertyContentRevision implements ContentRevision, MarkerVc
myUrl = url;
}
@Nullable
@Override
public String getContent() throws VcsException {
public List<PropertyData> getProperties() throws VcsException {
if (myContent == null) {
myContent = loadContent();
}
return myContent;
}
private String loadContent() {
@Override
public String getContent() throws VcsException {
return AbstractShowPropertiesDiffAction.toSortedStringPresentation(getProperties());
}
private List<PropertyData> loadContent() throws VcsException {
final SvnVcs vcs = SvnVcs.getInstance(myProject);
final Ref<String> ref = new Ref<String>();
final Ref<List<PropertyData>> ref = new Ref<List<PropertyData>>();
final Ref<VcsException> exceptionRef = new Ref<VcsException>();
final Runnable runnable = new Runnable() {
@Override
public void run() {
@@ -70,24 +81,21 @@ public class SvnLazyPropertyContentRevision implements ContentRevision, MarkerVc
ref.set(AbstractShowPropertiesDiffAction.getPropertyList(vcs, myUrl, ((SvnRevisionNumber)myNumber).getRevision()));
}
catch (VcsException e) {
// unknown node kind (node deleted)
/*if (e.getErrorMessage().getErrorCode().getCode() == 145000) {
return "";
}*/
ref.set("Can not get properties: " + e.getMessage());
exceptionRef.set(e);
}
}
};
if (ApplicationManager.getApplication().isDispatchThread()) {
final boolean completed = ProgressManager.getInstance()
.runProcessWithProgressSynchronously(runnable, SvnBundle.message("progress.title.loading.file.properties"), true, myProject);
if (! completed) {
return "Properties load for revision " + getRevisionNumber().asString() + " was canceled.";
if (!completed) {
throw new VcsException("Properties load for revision " + getRevisionNumber().asString() + " was canceled.");
}
}
else {
runnable.run();
}
if (!exceptionRef.isNull()) throw exceptionRef.get();
return ref.get();
}
@@ -52,4 +52,21 @@ public class PropertyValue {
public String toString() {
return myValue;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PropertyValue value = (PropertyValue)o;
if (!myValue.equals(value.myValue)) return false;
return true;
}
@Override
public int hashCode() {
return myValue.hashCode();
}
}
+2 -2
View File
@@ -92,6 +92,6 @@
</library>
</orderEntry>
<orderEntry type="library" name="http-client" level="project" />
<orderEntry type="module" module-name="diff-impl" />
</component>
</module>
</module>