short diff: Top | Bottom and Left | Right names for presentations;

better fragments marking;
navigation actions
This commit is contained in:
irengrig
2011-09-07 12:33:42 +04:00
parent aa4a91edae
commit a2c07e7b31
7 changed files with 301 additions and 51 deletions
@@ -235,7 +235,7 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid
((EditorGutterComponentEx) getEditor2().getGutter()).setLineNumberConvertor(newConvertor);
}
}
// todo pay attention here
private static DiffHighlighterFactory createHighlighter(FileType contentType, VirtualFile file, Project project) {
return new DiffHighlighterFactoryImpl(contentType, file, project);
}
@@ -307,8 +307,11 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid
}
private void updateStatusBar() {
int differentLineBlocks = getLineBlocks().getCount();
myPanel.setStatusBarText(DiffBundle.message("diff.count.differences.status.text", differentLineBlocks));
myPanel.setStatusBarText(getNumDifferencesText());
}
public String getNumDifferencesText() {
return DiffBundle.message("diff.count.differences.status.text", getLineBlocks().getCount());
}
public boolean hasDifferences() {
@@ -416,7 +419,7 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid
return getSideView(side).getEditor();
}
private DiffSideView getSideView(FragmentSide side) {
public DiffSideView getSideView(FragmentSide side) {
if (side == FragmentSide.SIDE1) {
return myLeftSide;
}
@@ -16,15 +16,15 @@ import java.awt.*;
* Time: 3:12 PM
*/
public class FragmentNumberGutterIconRenderer extends GutterIconRenderer {
private final int myNumber;
private final String myPresentation;
private CaptionIcon myIcon;
public FragmentNumberGutterIconRenderer(int number, final TextAttributesKey key, final Component component) {
myNumber = number;
public FragmentNumberGutterIconRenderer(String presentation, final TextAttributesKey key, final Component component) {
myPresentation = presentation;
final EditorColorsScheme globalScheme = EditorColorsManager.getInstance().getGlobalScheme();
final Color color = globalScheme.getAttributes(key).getBackgroundColor();
myIcon = new CaptionIcon(color, UIUtil.getButtonFont(), String.valueOf(number), component, CaptionIcon.Form.ROUNDED, false, false);
myIcon = new CaptionIcon(color, UIUtil.getButtonFont(), presentation, component, CaptionIcon.Form.ROUNDED, false, false);
}
@NotNull
@@ -40,13 +40,13 @@ public class FragmentNumberGutterIconRenderer extends GutterIconRenderer {
FragmentNumberGutterIconRenderer that = (FragmentNumberGutterIconRenderer)o;
if (myNumber != that.myNumber) return false;
if (!myPresentation.equals(that.myPresentation)) return false;
return true;
}
@Override
public int hashCode() {
return myNumber;
return myPresentation.hashCode();
}
}
@@ -58,6 +58,8 @@ public class FragmentedDiffPanelState extends DiffPanelState {
}
private LineBlocks addMarkup(final List<LineFragment> lines) {
myFragmentHighlighter.precalculateNumbers(lines);
for (Iterator<LineFragment> iterator = lines.iterator(); iterator.hasNext();) {
LineFragment line = iterator.next();
myFragmentHighlighter.setIsLast(!iterator.hasNext());
@@ -126,4 +128,12 @@ public class FragmentedDiffPanelState extends DiffPanelState {
}
myRanges.addAll(ranges);
}
public List<Integer> getLeftLines() {
return myFragmentHighlighter.getLeftLines();
}
public List<Integer> getRightLines() {
return myFragmentHighlighter.getRightLines();
}
}
@@ -4,9 +4,14 @@ import com.intellij.openapi.diff.DiffColors;
import com.intellij.openapi.diff.impl.FragmentNumberGutterIconRenderer;
import com.intellij.openapi.diff.impl.fragments.Fragment;
import com.intellij.openapi.diff.impl.fragments.FragmentHighlighterImpl;
import com.intellij.openapi.diff.impl.fragments.LineFragment;
import com.intellij.openapi.diff.impl.util.TextDiffTypeEnum;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.editor.markup.GutterIconRenderer;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.util.containers.MultiMap;
import java.util.*;
/**
* @author irengrig
@@ -15,23 +20,18 @@ import com.intellij.openapi.editor.markup.GutterIconRenderer;
*/
public class NumberedFragmentHighlighter extends FragmentHighlighterImpl {
private final boolean myDrawNumber;
private int myCounter;
private final Map<Integer, Pair<String, TextDiffTypeEnum>> myLeftPrecalculated;
private final Map<Integer, Pair<String, TextDiffTypeEnum>> myRightPrecalculated;
private int myPreviousLineLeft;
private int myPreviousLineRight;
public NumberedFragmentHighlighter(DiffMarkup appender1, DiffMarkup appender2, boolean drawNumber) {
super(appender1, appender2);
myDrawNumber = drawNumber;
myCounter = 0;
}
private GutterIconRenderer createRenderer(Fragment fragment) {
final TextAttributesKey key = getColorAttributesKey(fragment.getType());
if (key != null) {
final FragmentNumberGutterIconRenderer renderer =
new FragmentNumberGutterIconRenderer(myCounter + 1, key, myAppender1.getEditor().getScrollPane());
++ myCounter;
return renderer;
}
return null;
myLeftPrecalculated = new HashMap<Integer, Pair<String, TextDiffTypeEnum>>();
myRightPrecalculated = new HashMap<Integer, Pair<String, TextDiffTypeEnum>>();
myPreviousLineLeft = -1;
myPreviousLineRight = -1;
}
private TextAttributesKey getColorAttributesKey(final TextDiffTypeEnum textDiffTypeEnum) {
@@ -50,13 +50,103 @@ public class NumberedFragmentHighlighter extends FragmentHighlighterImpl {
@Override
protected void highlightFragmentImpl(Fragment fragment, boolean drawBorder) {
final GutterIconRenderer renderer = myDrawNumber ? createRenderer(fragment) : null;
if (! myDrawNumber || fragment.getType() == null || TextDiffTypeEnum.NONE.equals(fragment.getType())) {
myAppender1.highlightText(fragment, drawBorder, null);
myAppender2.highlightText(fragment, drawBorder, null);
return;
}
int lineLeft = myAppender1.getDocument().getLineNumber(fragment.getRange(FragmentSide.SIDE1).getStartOffset());
int lineRight = myAppender2.getDocument().getLineNumber(fragment.getRange(FragmentSide.SIDE2).getStartOffset());
Pair<String, TextDiffTypeEnum> left = myLeftPrecalculated.get(lineLeft);
if (myPreviousLineLeft == lineLeft || left == null) {
myAppender1.highlightText(fragment, drawBorder, null);
} else {
myAppender1.highlightText(fragment, drawBorder, new FragmentNumberGutterIconRenderer(left.getFirst(), getColorAttributesKey(left.getSecond()), myAppender1.getEditor().getScrollPane()));
myPreviousLineLeft = lineLeft;
}
myAppender1.highlightText(fragment, drawBorder, renderer);
myAppender2.highlightText(fragment, drawBorder, renderer);
Pair<String, TextDiffTypeEnum> right = myLeftPrecalculated.get(lineRight);
if (myPreviousLineRight == lineRight || right == null) {
myAppender2.highlightText(fragment, drawBorder, null);
} else {
myAppender2.highlightText(fragment, drawBorder, new FragmentNumberGutterIconRenderer(right.getFirst(), getColorAttributesKey(right.getSecond()), myAppender1.getEditor().getScrollPane()));
myPreviousLineRight = lineRight;
}
}
public void reset() {
myCounter = 0;
myLeftPrecalculated.clear();
myRightPrecalculated.clear();
myPreviousLineLeft = -1;
myPreviousLineRight = -1;
}
public void precalculateNumbers(List<LineFragment> lines) {
final MultiMap<Integer, Pair<Integer, TextDiffTypeEnum>> leftMap = new MultiMap<Integer, Pair<Integer, TextDiffTypeEnum>>();
final MultiMap<Integer, Pair<Integer, TextDiffTypeEnum>> rightMap = new MultiMap<Integer, Pair<Integer, TextDiffTypeEnum>>();
int cnt = 1;
for (LineFragment line : lines) {
if (line.getType() == null || TextDiffTypeEnum.NONE.equals(line.getType())) continue;
final Iterator<Fragment> iterator = line.getChildrenIterator();
if (iterator == null) continue;
while (iterator.hasNext()) {
final Fragment next = iterator.next();
if (next.getType() == null || TextDiffTypeEnum.NONE.equals(next.getType())) continue;
TextRange left = next.getRange(FragmentSide.SIDE1);
TextRange right = next.getRange(FragmentSide.SIDE2);
leftMap.putValue(myAppender1.getDocument().getLineNumber(left.getStartOffset()), new Pair<Integer, TextDiffTypeEnum>(cnt, next.getType()));
rightMap.putValue(myAppender2.getDocument().getLineNumber(right.getStartOffset()), new Pair<Integer, TextDiffTypeEnum>(cnt, next.getType()));
++ cnt;
}
}
// merge
merge(leftMap, myLeftPrecalculated);
merge(rightMap, myRightPrecalculated);
}
private void merge(MultiMap<Integer, Pair<Integer, TextDiffTypeEnum>> leftMap,
final Map<Integer, Pair<String, TextDiffTypeEnum>> whereTo) {
for (Map.Entry<Integer, Collection<Pair<Integer, TextDiffTypeEnum>>> entry : leftMap.entrySet()) {
List<Pair<Integer, TextDiffTypeEnum>> value = (List<Pair<Integer, TextDiffTypeEnum>>) entry.getValue();
if (value.size() > 1) {
Pair<Integer, TextDiffTypeEnum> pair1 = value.iterator().next();
Pair<Integer, TextDiffTypeEnum> pair2 = value.get(value.size() - 1);
TextDiffTypeEnum type = mergeDiffType(value);
whereTo.put(entry.getKey(), new Pair<String, TextDiffTypeEnum>(String.valueOf(pair1.getFirst()) + "-" +
String.valueOf(pair2.getFirst()), type));
} else {
Pair<Integer, TextDiffTypeEnum> pair = value.iterator().next();
whereTo.put(entry.getKey(), new Pair<String, TextDiffTypeEnum>(String.valueOf(pair.getFirst()), pair.getSecond()));
}
}
}
private TextDiffTypeEnum mergeDiffType(List<Pair<Integer, TextDiffTypeEnum>> value) {
TextDiffTypeEnum previous = null;
for (Pair<Integer, TextDiffTypeEnum> pair : value) {
if (previous == null) {
previous = pair.getSecond();
continue;
}
if (! previous.equals(pair.getSecond())) return TextDiffTypeEnum.CHANGED;
}
return previous;
}
public List<Integer> getLeftLines() {
List<Integer> list = new ArrayList<Integer>(myLeftPrecalculated.keySet());
Collections.sort(list);
return list;
}
public List<Integer> getRightLines() {
List<Integer> list = new ArrayList<Integer>(myRightPrecalculated.keySet());
Collections.sort(list);
return list;
}
}
@@ -24,12 +24,11 @@ import com.intellij.openapi.diff.SimpleContent;
import com.intellij.openapi.diff.ex.DiffPanelEx;
import com.intellij.openapi.diff.ex.DiffPanelOptions;
import com.intellij.openapi.diff.impl.DiffPanelImpl;
import com.intellij.openapi.diff.impl.DiffSideView;
import com.intellij.openapi.diff.impl.highlighting.DiffPanelState;
import com.intellij.openapi.diff.impl.highlighting.FragmentSide;
import com.intellij.openapi.diff.impl.highlighting.FragmentedDiffPanelState;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.editor.ScrollingModel;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.event.VisibleAreaEvent;
import com.intellij.openapi.editor.event.VisibleAreaListener;
import com.intellij.openapi.editor.ex.EditorEx;
@@ -48,11 +47,13 @@ import com.intellij.util.BeforeAfter;
import com.intellij.util.containers.Convertor;
import com.intellij.util.ui.ButtonlessScrollBarUI;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.MouseEvent;
import java.util.*;
import java.util.List;
@@ -74,8 +75,11 @@ public class ChangesFragmentedDiffPanel implements Disposable {
private DiffPanel myVertical;
private boolean myCurrentHorizontal;
private JPanel myTopPanel;
private List<Integer> myLeftLines;
private List<Integer> myRightLines;
private final MyNextDiffAction myNextDiff;
private final MyPreviousDiffAction myPreviousDiff;
// todo here
public ChangesFragmentedDiffPanel(final Project project, final FragmentedContent fragmentedContent,
final LinkedList<DiffPanel> cache, String filePath) {
myProject = project;
@@ -112,6 +116,8 @@ public class ChangesFragmentedDiffPanel implements Disposable {
return myPanel;
}
};
myNextDiff = new MyNextDiffAction();
myPreviousDiff = new MyPreviousDiffAction();
}
@Override
@@ -129,10 +135,22 @@ public class ChangesFragmentedDiffPanel implements Disposable {
final JPanel wrapper = new JPanel();
//final BoxLayout boxLayout = new BoxLayout(wrapper, BoxLayout.X_AXIS);
wrapper.setLayout(new BorderLayout());
final JLabel label = new JLabel(myFilePath);
label.setBorder(BorderFactory.createEmptyBorder(1,2,0,0));
wrapper.add(label, BorderLayout.WEST);
wrapper.add(new MyShowSettingsButton(), BorderLayout.EAST); // todo should be one button for all..
final JLabel titleLabel = new JLabel(myFilePath);
titleLabel.setBorder(BorderFactory.createEmptyBorder(1, 2, 0, 0));
wrapper.add(titleLabel, BorderLayout.WEST);
DefaultActionGroup dag = new DefaultActionGroup();
boolean navigationEnabled = !myFragmentedContent.isOneSide() && myFragmentedContent.getSize() > 1;
myNextDiff.setEnabled(navigationEnabled);
myPreviousDiff.setEnabled(navigationEnabled);
myPreviousDiff.copyShortcutFrom(ActionManager.getInstance().getAction("PreviousDiff"));
myNextDiff.copyShortcutFrom(ActionManager.getInstance().getAction("NextDiff"));
dag.add(myPreviousDiff);
dag.add(myNextDiff);
myPreviousDiff.registerCustomShortcutSet(myPreviousDiff.getShortcutSet(), myPanel);
myNextDiff.registerCustomShortcutSet(myNextDiff.getShortcutSet(), myPanel);
dag.add(new PopupAction());
ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, dag, true);
wrapper.add(toolbar.getComponent(), BorderLayout.EAST);
myTopPanel.add(wrapper, BorderLayout.CENTER);
@@ -176,6 +194,11 @@ public class ChangesFragmentedDiffPanel implements Disposable {
final DiffPanel diffPanel = createPanel(oldConvertor, newConvertor, sbOld, sbNew, ranges);
myCurrentHorizontal = myConfiguration.SHORT_DIFF_HORISONTALLY;
savePanel(diffPanel);
titleLabel.setText(titleText((DiffPanelImpl)diffPanel));
FragmentedDiffPanelState state = (FragmentedDiffPanelState)((DiffPanelImpl)diffPanel).getDiffPanelState();
myLeftLines = state.getLeftLines();
myRightLines = state.getRightLines();
myConfiguration.SHORT_DIFF_HORISONTALLY = ! myConfiguration.SHORT_DIFF_HORISONTALLY;
final DiffPanel diffPanel2 = createPanel(oldConvertor, newConvertor, sbOld, sbNew, ranges);
savePanel(diffPanel2);
@@ -185,6 +208,10 @@ public class ChangesFragmentedDiffPanel implements Disposable {
myPanel.add(diffPanel.getComponent(), BorderLayout.CENTER);
}
private String titleText(DiffPanelImpl diffPanel) {
return myFilePath + " " + diffPanel.getNumDifferencesText();
}
private void savePanel(DiffPanel diffPanel) {
if (myConfiguration.SHORT_DIFF_HORISONTALLY) {
myHorizontal = diffPanel;
@@ -426,7 +453,8 @@ public class ChangesFragmentedDiffPanel implements Disposable {
private AnAction myNumbered;
private PopupAction() {
myUsual = new AnAction("Vertical") {
super("Settings", "Settings", IconLoader.getIcon("/general/secondaryGroup.png"));
myUsual = new AnAction("Top | Bottom") {
@Override
public void actionPerformed(AnActionEvent e) {
boolean was = myConfiguration.SHORT_DIFF_HORISONTALLY;
@@ -436,7 +464,7 @@ public class ChangesFragmentedDiffPanel implements Disposable {
}
}
};
myNumbered = new AnAction("Horizontal") {
myNumbered = new AnAction("Left | Right") {
@Override
public void actionPerformed(AnActionEvent e) {
boolean was = myConfiguration.SHORT_DIFF_HORISONTALLY;
@@ -460,13 +488,19 @@ public class ChangesFragmentedDiffPanel implements Disposable {
final ListPopup popup = JBPopupFactory.getInstance().createActionGroupPopup(null, dag, e.getDataContext(),
JBPopupFactory.ActionSelectionAid.SPEEDSEARCH,
false);
final Dimension dimension = popup.getContent().getPreferredSize();
final Point at = new Point(-dimension.width / 2, 0);
popup.show(new RelativePoint(myParent, at));
if (e.getInputEvent() instanceof MouseEvent) {
popup.show(new RelativePoint((MouseEvent)e.getInputEvent()));
} else {
// todo correct
/*final Dimension dimension = popup.getContent().getPreferredSize();
final Point at = new Point(-dimension.width / 2, 0);
popup.show(new RelativePoint(myParent, at));*/
popup.showInBestPositionFor(e.getDataContext());
}
}
}
private class MyShowSettingsButton extends ActionButton implements DumbAware {
/*private class MyShowSettingsButton extends ActionButton implements DumbAware {
MyShowSettingsButton() {
this(new PopupAction(), new Presentation(), ActionPlaces.CHANGES_LOCAL_DIFF_SETTINGS, ActionToolbar.DEFAULT_MINIMUM_BUTTON_SIZE);
}
@@ -476,16 +510,11 @@ public class ChangesFragmentedDiffPanel implements Disposable {
((PopupAction) getAction()).setParent(this);
myPresentation.setIcon(IconLoader.getIcon("/general/secondaryGroup.png"));
}
}
}*/
private void ensurePresentation() {
if (myCurrentHorizontal != myConfiguration.SHORT_DIFF_HORISONTALLY) {
final DiffPanel panel;
if (myConfiguration.SHORT_DIFF_HORISONTALLY) {
panel = myHorizontal;
} else {
panel = myVertical;
}
final DiffPanel panel = getCurrentPanel();
myPanel.removeAll();
myPanel.add(myTopPanel, BorderLayout.NORTH);
@@ -496,4 +525,106 @@ public class ChangesFragmentedDiffPanel implements Disposable {
myCurrentHorizontal = myConfiguration.SHORT_DIFF_HORISONTALLY;
}
}
private DiffPanel getCurrentPanel() {
DiffPanel panel;
if (myConfiguration.SHORT_DIFF_HORISONTALLY) {
panel = myHorizontal;
} else {
panel = myVertical;
}
return panel;
}
private int getCurrentLogicalLineIdx(final boolean forward) {
assert ! myLeftLines.isEmpty();
final Editor editor = ((DiffPanelImpl) getCurrentPanel()).getEditor1();
Point location = editor.getScrollingModel().getVisibleArea().getLocation();
LogicalPosition lp = editor.xyToLogicalPosition(location);
int line = lp.line;
if (forward) {
if (line >= myLeftLines.get(myLeftLines.size() - 1)) {
return myLeftLines.size() - 1;
}
for (int i = myLeftLines.size() - 1; i >= 0; i--) {
Integer integer = myLeftLines.get(i);
if (integer <= line) return i + 1;
}
return 0;
} else {
if (line <= myLeftLines.get(0)) return 0;
for (int i = 0; i < myLeftLines.size(); i++) {
Integer integer = myLeftLines.get(i);
if (integer > line) {
return i;
}
}
return myLeftLines.size() - 1;
}
}
private class MyPreviousDiffAction extends AnAction {
private boolean myEnabled;
private MyPreviousDiffAction() {
super("Previous Change", "Previous Change", IconLoader.getIcon("/actions/previousOccurence.png"));
}
@Override
public void actionPerformed(AnActionEvent e) {
int currentLogicalLineIdx = getCurrentLogicalLineIdx(false);
int nextLineIdx = currentLogicalLineIdx == 0 ? 0 : currentLogicalLineIdx - 1;
int rightIndex = nextLineIdx >= myRightLines.size() ? (myRightLines.size() - 1) : nextLineIdx;
DiffPanelImpl panel = (DiffPanelImpl) getCurrentPanel();
panel.getSideView(FragmentSide.SIDE1).scrollToFirstDiff(myLeftLines.get(nextLineIdx));
panel.getSideView(FragmentSide.SIDE2).scrollToFirstDiff(myRightLines.get(rightIndex));
}
public void setEnabled(boolean enabled) {
myEnabled = enabled;
}
@Override
public void update(AnActionEvent e) {
super.update(e);
e.getPresentation().setEnabled(myEnabled);
}
}
private class MyNextDiffAction extends AnAction {
private boolean myEnabled;
private MyNextDiffAction() {
// todo text
super("Next Change", "Next Change", IconLoader.getIcon("/actions/nextOccurence.png"));
}
@Override
public void actionPerformed(AnActionEvent e) {
int currentLogicalLineIdx = getCurrentLogicalLineIdx(true);
int nextLineIdx = currentLogicalLineIdx == (myLeftLines.size() - 1) ? currentLogicalLineIdx : currentLogicalLineIdx + 1;
int rightIndex = nextLineIdx >= myRightLines.size() ? (myRightLines.size() - 1) : nextLineIdx;
DiffPanelImpl panel = (DiffPanelImpl) getCurrentPanel();
panel.getSideView(FragmentSide.SIDE1).scrollToFirstDiff(myLeftLines.get(nextLineIdx));
panel.getSideView(FragmentSide.SIDE2).scrollToFirstDiff(myRightLines.get(rightIndex));
}
public void setEnabled(boolean enabled) {
myEnabled = enabled;
}
@Override
public void update(AnActionEvent e) {
super.update(e);
e.getPresentation().setEnabled(myEnabled);
}
}
/* <action id="VcsShowNextChangeMarker" class="com.intellij.openapi.vcs.actions.ShowNextChangeMarkerAction"
icon="/actions/nextOccurence.png"/>
<action id="VcsShowPrevChangeMarker" class="com.intellij.openapi.vcs.actions.ShowPrevChangeMarkerAction"
icon="/actions/previousOccurence.png"/>
/**/
}
@@ -48,4 +48,12 @@ public class FragmentedContent {
public List<BeforeAfter<TextRange>> getRanges() {
return myRanges;
}
public int getSize() {
return myRanges.size();
}
public boolean isOneSide() {
return myRanges.size() == 1 && (myRanges.get(0).getAfter().isEmpty() || myRanges.get(0).getBefore().isEmpty());
}
}
@@ -33,6 +33,8 @@ import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vcs.Details;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.VcsKey;
import com.intellij.openapi.vcs.history.ShortVcsRevisionNumber;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
import com.intellij.util.BeforeAfter;
import com.intellij.util.containers.HashMap;
@@ -302,6 +304,12 @@ public class VcsChangeDetailsManager {
private static String changeDescription(Change o) {
return new StringBuilder().append(ChangesUtil.getFilePath(o).getName()).append(" (").append(
o.getBeforeRevision() == null
? "New" : o.getBeforeRevision().getRevisionNumber().asString()).append(")").toString();
? "New" : beforeRevisionText(o)).append(")").toString();
}
private static String beforeRevisionText(Change o) {
VcsRevisionNumber revisionNumber = o.getBeforeRevision().getRevisionNumber();
return revisionNumber instanceof ShortVcsRevisionNumber ? ((ShortVcsRevisionNumber) revisionNumber).toShortString() :
revisionNumber.asString();
}
}