Merge "AbstractDaemonTest: Add user SSH session"
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/Groups.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/Groups.java
index 608abd6..3b31000 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/Groups.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/Groups.java
@@ -174,31 +174,31 @@
     public String getSuggest() {
       return suggest;
     }
+  }
 
-    /**
-     * A default implementation which allows source compatibility
-     * when adding new methods to the interface.
-     **/
-    class NotImplemented implements Groups {
-      @Override
-      public GroupApi id(String id) throws RestApiException {
-        throw new NotImplementedException();
-      }
+  /**
+   * A default implementation which allows source compatibility
+   * when adding new methods to the interface.
+   **/
+  class NotImplemented implements Groups {
+    @Override
+    public GroupApi id(String id) throws RestApiException {
+      throw new NotImplementedException();
+    }
 
-      @Override
-      public GroupApi create(String name) throws RestApiException {
-        throw new NotImplementedException();
-      }
+    @Override
+    public GroupApi create(String name) throws RestApiException {
+      throw new NotImplementedException();
+    }
 
-      @Override
-      public GroupApi create(GroupInput input) throws RestApiException {
-        throw new NotImplementedException();
-      }
+    @Override
+    public GroupApi create(GroupInput input) throws RestApiException {
+      throw new NotImplementedException();
+    }
 
-      @Override
-      public ListRequest list() {
-        throw new NotImplementedException();
-      }
+    @Override
+    public ListRequest list() {
+      throw new NotImplementedException();
     }
   }
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
index 157f947..b2334d1d 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
@@ -62,25 +62,17 @@
   String patchTableColumnName();
   String patchTableColumnComments();
   String patchTableColumnSize();
-  String patchTableColumnDiff();
-  String patchTableDiffSideBySide();
-  String patchTableDiffUnified();
   String commitMessage();
 
   String patchTablePrev();
   String patchTableNext();
   String patchTableOpenDiff();
-  String patchTableOpenUnifiedDiff();
-  String upToChangeIconLink();
-  String prevPatchLinkIcon();
-  String nextPatchLinkIcon();
 
   String approvalTableAddReviewerHint();
   String approvalTableAddManyReviewersConfirmationDialogTitle();
 
   String changeInfoBlockUploaded();
   String changeInfoBlockUpdated();
-  String changePermalink();
 
   String messageNoAuthor();
 
@@ -108,7 +100,6 @@
   String pagedChangeListPrev();
   String pagedChangeListNext();
 
-  String reviewed();
   String submitFailed();
 
   String votable();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
index 7fe3675..b7e2677 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
@@ -44,22 +44,17 @@
 patchTableColumnName = File Path
 patchTableColumnComments = Comments
 patchTableColumnSize = Size
-patchTableColumnDiff = Diff
-patchTableDiffSideBySide = Side-by-Side
-patchTableDiffUnified = Unified
 commitMessage = Commit Message
 
 patchTablePrev = Previous file
 patchTableNext = Next file
 patchTableOpenDiff = Open diff
-patchTableOpenUnifiedDiff = Open unified diff
 
 approvalTableAddReviewerHint = Name or Email or Group
 approvalTableAddManyReviewersConfirmationDialogTitle = Adding Group Members as Reviewers
 
 changeInfoBlockUploaded = Uploaded
 changeInfoBlockUpdated = Updated
-changePermalink = Permalink
 
 messageNoAuthor = Gerrit Code Review
 
@@ -87,11 +82,6 @@
 pagedChangeListPrev = ⇦Prev
 pagedChangeListNext = Next⇨
 
-upToChangeIconLink = ⇧Up to change
-prevPatchLinkIcon = ⇦
-nextPatchLinkIcon = ⇨
-
-reviewed = Reviewed
 submitFailed = Submit Failed
 
 votable = Votable:
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java
index e1dc0a3..c5397ee 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java
@@ -18,53 +18,28 @@
 
 public interface ChangeMessages extends Messages {
   String accountDashboardTitle(String fullName);
-  String changesOpenInProject(String string);
-  String changesMergedInProject(String string);
-  String changesAbandonedInProject(String string);
 
   String revertChangeDefaultMessage(String commitMsg, String commitId);
 
   String cherryPickedChangeDefaultMessage(String commitMsg, String commitId);
   String changeScreenTitleId(String changeId);
-  String outdatedHeader(int outdated);
-  String patchSetHeader(int id);
   String loadingPatchSet(int id);
-  String submitPatchSet(int id);
 
-  String patchTableComments(@PluralCount int count);
-  String patchTableDrafts(@PluralCount int count);
   String patchTableSize_Modify(int insertions, int deletions);
   String patchTableSize_ModifyBinaryFiles(String bytesInserted,
       String bytesDeleted);
   String patchTableSize_ModifyBinaryFilesWithPercentages(String bytesInserted,
       String percentageInserted, String bytesDeleted, String percentageDeleted);
   String patchTableSize_LongModify(int insertions, int deletions);
-  String patchTableSize_Lines(@PluralCount int insertions);
 
-  String removeHashtag(String name);
   String removeReviewer(String fullName);
   String removeVote(String label);
-  String messageWrittenOn(String date);
-
-  String renamedFrom(String sourcePath);
-  String copiedFrom(String sourcePath);
-  String otherFrom(String sourcePath);
 
   String blockedOn(String labelName);
   String needs(String labelName);
-  String publishComments(String changeId, int ps);
-  String lineHeader(int line);
 
   String changeQueryWindowTitle(String query);
   String changeQueryPageTitle(String query);
 
-  String reviewerNotFound(String who);
-  String accountInactive(String who);
-  String changeNotVisibleTo(String who);
-  String groupIsEmpty(String group);
-  String groupIsNotAllowed(String group);
-  String groupHasTooManyMembers(String group);
-  String groupManyMembersConfirmation(String group, int memberCount);
-
   String insertionsAndDeletions(int insertions, int deletions);
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties
index 1fe5909..f0d7e59 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties
@@ -1,50 +1,25 @@
 # Changes to this file should also be made in
 # gerrit-server/src/main/resources/com/google/gerrit/server/change/ChangeMessages.properties
 accountDashboardTitle = Code Review Dashboard for {0}
-changesOpenInProject = Open Changes In {0}
-changesMergedInProject = Merged Changes In {0}
-changesAbandonedInProject = Abandoned Changes In {0}
 
 revertChangeDefaultMessage = Revert \"{0}\"\n\nThis reverts commit {1}.
 cherryPickedChangeDefaultMessage = {0}\n(cherry picked from commit {1})
 
 changeScreenTitleId = Change {0}
-outdatedHeader = Change depends on {0} outdated change(s) and should be rebased on the latest patch sets.
-patchSetHeader = Patch Set {0}
 loadingPatchSet = Loading Patch Set {0} ...
-submitPatchSet = Submit Patch Set {0}
 
-patchTableComments = {0} comments
-patchTableDrafts = {0} drafts
 patchTableSize_Modify = +{0}, -{1}
 patchTableSize_ModifyBinaryFiles = +{0}, -{1}
 patchTableSize_ModifyBinaryFilesWithPercentages = +{0} (+{1}), -{2} (-{3})
 patchTableSize_LongModify = {0} inserted, {1} deleted
-patchTableSize_Lines = {0} lines
 
-removeHashtag = Remove hashtag {0}
 removeReviewer = Remove reviewer {0}
 removeVote = Remove vote {0}
-messageWrittenOn = on {0}
-
-renamedFrom = renamed from {0}
-copiedFrom = copied from {0}
-otherFrom = from {0}
 
 blockedOn = Blocked on {0} Label
 needs = Needs {0} Label
-publishComments = Change {0} - Patch Set {1}: Publish Comments
-lineHeader = Line {0}:
 
 changeQueryWindowTitle = {0}
 changeQueryPageTitle = Search for {0}
 
-reviewerNotFound = {0} is neither a registered user nor a group.
-accountInactive = {0} is not an active user.
-changeNotVisibleTo = {0} cannot access the change.
-groupIsEmpty = The group {0} does not have any members to add as reviewers.
-groupIsNotAllowed =  The group {0} cannot be added as reviewer.
-groupHasTooManyMembers = The group {0} has too many members to add them all as reviewers.
-groupManyMembersConfirmation = The group {0} has {1} members. Do you want to add them all as reviewers?
-
 insertionsAndDeletions = +{0}, -{1}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages_en.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages_en.properties
deleted file mode 100644
index d9c7059..0000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages_en.properties
+++ /dev/null
@@ -1,8 +0,0 @@
-patchTableComments[one] = 1 comment
-patchTableComments = {0} comments
-
-patchTableDrafts[one] = 1 draft
-patchTableDrafts = {0} drafts
-
-patchTableSize_Lines[one] = 1 line
-patchTableSize_Lines = {0} lines
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ChunkManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ChunkManager.java
index 91533ab..3c067ee0 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ChunkManager.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ChunkManager.java
@@ -25,7 +25,6 @@
 import net.codemirror.lib.TextMarker;
 
 import java.util.ArrayList;
-import java.util.Comparator;
 import java.util.List;
 
 /** Colors modified regions for {@link SideBySide} and {@link Unified}. */
@@ -33,23 +32,15 @@
   static final native void onClick(Element e, JavaScriptObject f)
   /*-{ e.onclick = f }-*/;
 
-  private final Scrollbar scrollbar;
-  private final LineMapper mapper;
+  final Scrollbar scrollbar;
+  final LineMapper lineMapper;
 
   private List<TextMarker> markers;
   private List<Runnable> undo;
 
   ChunkManager(Scrollbar scrollbar) {
     this.scrollbar = scrollbar;
-    this.mapper = new LineMapper();
-  }
-
-  LineMapper getLineMapper() {
-    return mapper;
-  }
-
-  Scrollbar getScrollbar() {
-    return scrollbar;
+    this.lineMapper = new LineMapper();
   }
 
   abstract DiffChunkInfo getFirst();
@@ -59,7 +50,7 @@
   }
 
   void reset() {
-    mapper.reset();
+    lineMapper.reset();
     for (TextMarker m : markers) {
       m.clear();
     }
@@ -110,7 +101,7 @@
 
     DiffChunkInfo lookUp = chunks.get(res);
     // If edit, skip the deletion chunk and set focus on the insertion one.
-    if (lookUp.isEdit() && lookUp.getSide() == A) {
+    if (lookUp.edit && lookUp.side == A) {
       res = res + (dir == Direction.PREV ? -1 : 1);
       if (res < 0 || chunks.size() <= res) {
         return;
@@ -118,8 +109,8 @@
     }
 
     DiffChunkInfo target = chunks.get(res);
-    CodeMirror targetCm = host.getCmFromSide(target.getSide());
-    int cmLine = getCmLine(target.getStart(), target.getSide());
+    CodeMirror targetCm = host.getCmFromSide(target.side);
+    int cmLine = getCmLine(target.start, target.side);
     targetCm.setCursor(Pos.create(cmLine));
     targetCm.focus();
     targetCm.scrollToY(
@@ -127,28 +118,5 @@
         - 0.5 * targetCm.scrollbarV().getClientHeight());
   }
 
-  Comparator<DiffChunkInfo> getDiffChunkComparator() {
-    // Chunks are ordered by their starting line. If it's a deletion,
-    // use its corresponding line on the revision side for comparison.
-    // In the edit case, put the deletion chunk right before the
-    // insertion chunk. This placement guarantees well-ordering.
-    return new Comparator<DiffChunkInfo>() {
-      @Override
-      public int compare(DiffChunkInfo a, DiffChunkInfo b) {
-        if (a.getSide() == b.getSide()) {
-          return a.getStart() - b.getStart();
-        } else if (a.getSide() == A) {
-          int comp = mapper.lineOnOther(a.getSide(), a.getStart())
-              .getLine() - b.getStart();
-          return comp == 0 ? -1 : comp;
-        } else {
-          int comp = a.getStart() -
-              mapper.lineOnOther(b.getSide(), b.getStart()).getLine();
-          return comp == 0 ? 1 : comp;
-        }
-      }
-    };
-  }
-
   abstract int getCmLine(int line, DisplaySide side);
 }
\ No newline at end of file
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffChunkInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffChunkInfo.java
index 1e5c5e5..4725b1e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffChunkInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffChunkInfo.java
@@ -15,32 +15,39 @@
 package com.google.gerrit.client.diff;
 
 /** Object recording the position of a diff chunk and whether it's an edit */
-class DiffChunkInfo {
-  private DisplaySide side;
-  private int start;
-  private int end;
-  private boolean edit;
+class DiffChunkInfo implements Comparable<DiffChunkInfo> {
+  final DisplaySide side;
+  final int start;
+  final int end;
+  final boolean edit;
 
-  DiffChunkInfo(DisplaySide side, int start, int end, boolean edit) {
+  private final int startOnOther;
+
+  DiffChunkInfo(DisplaySide side, int start, int startOnOther, int end,
+      boolean edit) {
     this.side = side;
     this.start = start;
+    this.startOnOther = startOnOther;
     this.end = end;
     this.edit = edit;
   }
 
-  DisplaySide getSide() {
-    return side;
-  }
-
-  int getStart() {
-    return start;
-  }
-
-  int getEnd() {
-    return end;
-  }
-
-  boolean isEdit() {
-    return edit;
+  /**
+   * Chunks are ordered by their starting line. If it's a deletion, use its
+   * corresponding line on the revision side for comparison. In the edit case,
+   * put the deletion chunk right before the insertion chunk. This placement
+   * guarantees well-ordering.
+   */
+  @Override
+  public int compareTo(DiffChunkInfo o) {
+    if (side == o.side) {
+      return start - o.start;
+    } else if (side == DisplaySide.A) {
+      int comp = startOnOther - o.start;
+      return comp == 0 ? -1 : comp;
+    } else {
+      int comp = start - o.startOnOther;
+      return comp == 0 ? 1 : comp;
+    }
   }
 }
\ No newline at end of file
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffScreen.java
index 01d6b16..a185105 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffScreen.java
@@ -99,7 +99,7 @@
   final PatchSet.Id revision;
   final String path;
   final DiffPreferences prefs;
-  final DiffView diffScreenType;
+  final SkipManager skipManager;
 
   private DisplaySide startSide;
   private int startLine;
@@ -133,13 +133,13 @@
     this.path = path;
     this.startSide = startSide;
     this.startLine = startLine;
-    this.diffScreenType = diffScreenType;
 
     prefs = DiffPreferences.create(Gerrit.getDiffPreferences());
     handlers = new ArrayList<>(6);
     keysNavigation = new KeyCommandSet(Gerrit.C.sectionNavigation());
     header = new Header(
         keysNavigation, base, revision, path, diffScreenType, prefs);
+    skipManager = new SkipManager(this);
   }
 
   @Override
@@ -600,8 +600,8 @@
     operation(new Runnable() {
       @Override
       public void run() {
-        getSkipManager().removeAll();
-        getSkipManager().render(context, diff);
+        skipManager.removeAll();
+        skipManager.render(context, diff);
         updateRenderEntireFile();
       }
     });
@@ -692,8 +692,6 @@
 
   abstract CommentManager getCommentManager();
 
-  abstract SkipManager getSkipManager();
-
   Change.Status getChangeStatus() {
     return changeStatus;
   }
@@ -875,12 +873,12 @@
             operation(new Runnable() {
               @Override
               public void run() {
-                getSkipManager().removeAll();
+                skipManager.removeAll();
                 getChunkManager().reset();
                 getDiffTable().scrollbar.removeDiffAnnotations();
                 setShowIntraline(prefs.intralineDifference());
                 render(diff);
-                getSkipManager().render(prefs.context(), diff);
+                skipManager.render(prefs.context(), diff);
               }
             });
           }
@@ -937,9 +935,11 @@
   abstract int getCmLine(int line, DisplaySide side);
 
   LineOnOtherInfo lineOnOther(DisplaySide side, int line) {
-    return getChunkManager().getLineMapper().lineOnOther(side, line);
+    return getChunkManager().lineMapper.lineOnOther(side, line);
   }
 
   abstract ScreenLoadCallback<ConfigInfoCache.Entry> getScreenLoadCallback(
       CommentsCollections comments);
+
+  abstract boolean isSideBySide();
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.java
index 9f106ce..216115a 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.client.patches.PatchUtil;
 import com.google.gerrit.client.rpc.Natives;
 import com.google.gerrit.client.ui.InlineHyperlink;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DiffView;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Patch;
 import com.google.gerrit.reviewdb.client.PatchSet;
@@ -148,7 +147,7 @@
     PatchSet.Id revision = sideA ? other.idActive : id;
 
     return new InlineHyperlink(label,
-        parent.diffScreenType == DiffView.SIDE_BY_SIDE
+        parent.isSideBySide()
             ? Dispatcher.toSideBySide(diffBase, revision, path)
             : Dispatcher.toUnified(diffBase, revision, path));
   }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java
index fa7e0e4..99b0a58 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java
@@ -66,7 +66,6 @@
 
   private SideBySideChunkManager chunkManager;
   private SideBySideCommentManager commentManager;
-  private SideBySideSkipManager skipManager;
 
   public SideBySide(
       PatchSet.Id base,
@@ -118,12 +117,12 @@
     if (getStartLine() == 0) {
       DiffChunkInfo d = chunkManager.getFirst();
       if (d != null) {
-        if (d.isEdit() && d.getSide() == DisplaySide.A) {
+        if (d.edit && d.side == DisplaySide.A) {
           setStartSide(DisplaySide.B);
-          setStartLine(lineOnOther(d.getSide(), d.getStart()).getLine() + 1);
+          setStartLine(lineOnOther(d.side, d.start).getLine() + 1);
         } else {
-          setStartSide(d.getSide());
-          setStartLine(d.getStart() + 1);
+          setStartSide(d.side);
+          setStartLine(d.start + 1);
         }
       }
     }
@@ -197,7 +196,6 @@
     setShowTabs(prefs.showTabs());
 
     chunkManager = new SideBySideChunkManager(this, cmA, cmB, diffTable.scrollbar);
-    skipManager = new SideBySideSkipManager(this, commentManager);
 
     operation(new Runnable() {
       @Override
@@ -217,7 +215,7 @@
     registerCmEvents(cmA);
     registerCmEvents(cmB);
     scrollSynchronizer = new ScrollSynchronizer(diffTable, cmA, cmB,
-            chunkManager.getLineMapper());
+            chunkManager.lineMapper);
 
     setPrefsAction(new PreferencesAction(this, prefs));
     header.init(getPrefsAction(), getUnifiedDiffLink(), diff.sideBySideWebLinks());
@@ -418,7 +416,7 @@
   }
 
   @Override
-  SideBySideSkipManager getSkipManager() {
-    return skipManager;
+  boolean isSideBySide() {
+    return true;
   }
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideChunkManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideChunkManager.java
index cbbafad..9b8a6aa 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideChunkManager.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideChunkManager.java
@@ -105,8 +105,6 @@
   void render(DiffInfo diff) {
     super.render();
 
-    LineMapper mapper = getLineMapper();
-
     chunks = new ArrayList<>();
     padding = new ArrayList<>();
     paddingDivs = new ArrayList<>();
@@ -117,11 +115,11 @@
 
     for (Region current : Natives.asList(diff.content())) {
       if (current.ab() != null) {
-        mapper.appendCommon(current.ab().length());
+        lineMapper.appendCommon(current.ab().length());
       } else if (current.skip() > 0) {
-        mapper.appendCommon(current.skip());
+        lineMapper.appendCommon(current.skip());
       } else if (current.common()) {
-        mapper.appendCommon(current.b().length());
+        lineMapper.appendCommon(current.b().length());
       } else {
         render(current, diffColor);
       }
@@ -148,10 +146,8 @@
   }
 
   private void render(Region region, String diffColor) {
-    LineMapper mapper = getLineMapper();
-
-    int startA = mapper.getLineA();
-    int startB = mapper.getLineB();
+    int startA = lineMapper.getLineA();
+    int startB = lineMapper.getLineB();
 
     JsArrayString a = region.a();
     JsArrayString b = region.b();
@@ -169,20 +165,19 @@
     addPadding(cmA, startA + aLen - 1, bLen - aLen);
     addPadding(cmB, startB + bLen - 1, aLen - bLen);
     addGutterTag(region, startA, startB);
-    mapper.appendReplace(aLen, bLen);
+    lineMapper.appendReplace(aLen, bLen);
 
-    int endA = mapper.getLineA() - 1;
-    int endB = mapper.getLineB() - 1;
+    int endA = lineMapper.getLineA() - 1;
+    int endB = lineMapper.getLineB() - 1;
     if (aLen > 0) {
-      addDiffChunk(cmB, endA, aLen, bLen > 0);
+      addDiffChunk(cmB, endB, endA, aLen, bLen > 0);
     }
     if (bLen > 0) {
-      addDiffChunk(cmA, endB, bLen, aLen > 0);
+      addDiffChunk(cmA, endA, endB, bLen, aLen > 0);
     }
   }
 
   private void addGutterTag(Region region, int startA, int startB) {
-    Scrollbar scrollbar = getScrollbar();
     if (region.a() == null) {
       scrollbar.insert(cmB, startB, region.b().length());
     } else if (region.b() == null) {
@@ -250,10 +245,10 @@
     }
   }
 
-  private void addDiffChunk(CodeMirror cmToPad, int lineOnOther,
+  private void addDiffChunk(CodeMirror cmToPad, int line, int lineOnOther,
       int chunkSize, boolean edit) {
     chunks.add(new DiffChunkInfo(host.otherCm(cmToPad).side(),
-        lineOnOther - chunkSize + 1, lineOnOther, edit));
+        lineOnOther - chunkSize + 1, line - chunkSize + 1, lineOnOther, edit));
   }
 
   @Override
@@ -266,8 +261,7 @@
             : 0;
         int res = Collections.binarySearch(
                 chunks,
-                new DiffChunkInfo(cm.side(), line, 0, false),
-                getDiffChunkComparator());
+                new DiffChunkInfo(cm.side(), line, 0, 0, false));
         diffChunkNavHelper(chunks, host, res, dir);
       }
     };
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideCommentManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideCommentManager.java
index cf9dd0f..e18f7a5 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideCommentManager.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideCommentManager.java
@@ -169,7 +169,7 @@
   @Override
   void insertNewDraft(DisplaySide side, int line) {
     if (line == 0) {
-      host.getSkipManager().ensureFirstLineIsVisible();
+      host.skipManager.ensureFirstLineIsVisible();
     }
 
     SideBySideCommentGroup group = group(side, line);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideSkipBar.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideSkipBar.java
deleted file mode 100644
index c4587a0..0000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideSkipBar.java
+++ /dev/null
@@ -1,217 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// 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.google.gerrit.client.diff;
-
-import com.google.gerrit.client.patches.PatchUtil;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Style.Unit;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.resources.client.CssResource;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.HTMLPanel;
-
-import net.codemirror.lib.CodeMirror;
-import net.codemirror.lib.Configuration;
-import net.codemirror.lib.LineWidget;
-import net.codemirror.lib.Pos;
-import net.codemirror.lib.TextMarker;
-import net.codemirror.lib.TextMarker.FromTo;
-
-/** The Widget that handles expanding of skipped lines */
-class SideBySideSkipBar extends SkipBar {
-  interface Binder extends UiBinder<HTMLPanel, SideBySideSkipBar> {}
-  private static final Binder uiBinder = GWT.create(Binder.class);
-  private static final int NUM_ROWS_TO_EXPAND = 10;
-  private static final int UP_DOWN_THRESHOLD = 30;
-
-  interface SkipBarStyle extends CssResource {
-    String noExpand();
-  }
-
-  @UiField(provided = true) Anchor skipNum;
-  @UiField(provided = true) Anchor upArrow;
-  @UiField(provided = true) Anchor downArrow;
-  @UiField SkipBarStyle style;
-
-  private final SideBySideSkipManager manager;
-  private final CodeMirror cm;
-
-  private LineWidget lineWidget;
-  private TextMarker textMarker;
-  private SideBySideSkipBar otherBar;
-
-  SideBySideSkipBar(SideBySideSkipManager manager, final CodeMirror cm) {
-    this.manager = manager;
-    this.cm = cm;
-
-    skipNum = new Anchor(true);
-    upArrow = new Anchor(true);
-    downArrow = new Anchor(true);
-    initWidget(uiBinder.createAndBindUi(this));
-    addDomHandler(new ClickHandler() {
-      @Override
-      public void onClick(ClickEvent event) {
-        cm.focus();
-      }
-    }, ClickEvent.getType());
-  }
-
-  void collapse(int start, int end, boolean attach) {
-    if (attach) {
-      boolean isNew = lineWidget == null;
-      Configuration cfg = Configuration.create()
-          .set("coverGutter", true)
-          .set("noHScroll", true);
-      if (start == 0) { // First line workaround
-        lineWidget = cm.addLineWidget(end + 1, getElement(), cfg.set("above", true));
-      } else {
-        lineWidget = cm.addLineWidget(start - 1, getElement(), cfg);
-      }
-      if (isNew) {
-        lineWidget.onFirstRedraw(new Runnable() {
-          @Override
-          public void run() {
-            int w = cm.getGutterElement().getOffsetWidth();
-            getElement().getStyle().setPaddingLeft(w, Unit.PX);
-          }
-        });
-      }
-    }
-
-    textMarker = cm.markText(
-        Pos.create(start, 0),
-        Pos.create(end),
-        Configuration.create()
-          .set("collapsed", true)
-          .set("inclusiveLeft", true)
-          .set("inclusiveRight", true));
-
-    textMarker.on("beforeCursorEnter", new Runnable() {
-      @Override
-      public void run() {
-        expandAll();
-      }
-    });
-
-    int skipped = end - start + 1;
-    if (skipped <= UP_DOWN_THRESHOLD) {
-      addStyleName(style.noExpand());
-    } else {
-      upArrow.setHTML(PatchUtil.M.expandBefore(NUM_ROWS_TO_EXPAND));
-      downArrow.setHTML(PatchUtil.M.expandAfter(NUM_ROWS_TO_EXPAND));
-    }
-    skipNum.setText(PatchUtil.M.patchSkipRegion(Integer
-        .toString(skipped)));
-  }
-
-  static void link(SideBySideSkipBar barA, SideBySideSkipBar barB) {
-    barA.otherBar = barB;
-    barB.otherBar = barA;
-  }
-
-  private void clearMarkerAndWidget() {
-    textMarker.clear();
-    lineWidget.clear();
-  }
-
-  @Override
-  void expandBefore(int cnt) {
-    expandSideBefore(cnt);
-
-    if (otherBar != null) {
-      otherBar.expandSideBefore(cnt);
-    }
-  }
-
-  private void expandSideBefore(int cnt) {
-    FromTo range = textMarker.find();
-    int oldStart = range.from().line();
-    int newStart = oldStart + cnt;
-    int end = range.to().line();
-    clearMarkerAndWidget();
-    collapse(newStart, end, true);
-    updateSelection();
-  }
-
-  @Override
-  void expandSideAll() {
-    clearMarkerAndWidget();
-    removeFromParent();
-  }
-
-  private void expandAfter() {
-    FromTo range = textMarker.find();
-    int start = range.from().line();
-    int oldEnd = range.to().line();
-    int newEnd = oldEnd - NUM_ROWS_TO_EXPAND;
-    boolean attach = start == 0;
-    if (attach) {
-      clearMarkerAndWidget();
-    } else {
-      textMarker.clear();
-    }
-    collapse(start, newEnd, attach);
-    updateSelection();
-  }
-
-  private void updateSelection() {
-    if (cm.somethingSelected()) {
-      FromTo sel = cm.getSelectedRange();
-      cm.setSelection(sel.from(), sel.to());
-    }
-  }
-
-  @UiHandler("skipNum")
-  void onExpandAll(@SuppressWarnings("unused") ClickEvent e) {
-    expandAll();
-    updateSelection();
-    if (otherBar != null) {
-      otherBar.expandAll();
-      otherBar.updateSelection();
-    }
-    cm.focus();
-  }
-
-  private void expandAll() {
-    expandSideAll();
-    if (otherBar != null) {
-      otherBar.expandSideAll();
-    }
-    manager.remove(this, otherBar);
-  }
-
-  @UiHandler("upArrow")
-  void onExpandBefore(@SuppressWarnings("unused") ClickEvent e) {
-    expandBefore(NUM_ROWS_TO_EXPAND);
-    if (otherBar != null) {
-      otherBar.expandBefore(NUM_ROWS_TO_EXPAND);
-    }
-    cm.focus();
-  }
-
-  @UiHandler("downArrow")
-  void onExpandAfter(@SuppressWarnings("unused") ClickEvent e) {
-    expandAfter();
-
-    if (otherBar != null) {
-      otherBar.expandAfter();
-    }
-    cm.focus();
-  }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideSkipBar.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideSkipBar.ui.xml
deleted file mode 100644
index d51f3d8..0000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideSkipBar.ui.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-Copyright (C) 2013 The Android Open Source Project
-
-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.
--->
-<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
-    xmlns:g='urn:import:com.google.gwt.user.client.ui'>
-  <ui:style gss='false' type='com.google.gerrit.client.diff.SideBySideSkipBar.SkipBarStyle'>
-    .skipBar {
-      background-color: #def;
-      height: 1.3em;
-      overflow: hidden;
-    }
-    .text {
-      display: table;
-      margin: 0 auto;
-      color: #777;
-      font-style: italic;
-      overflow: hidden;
-    }
-    .anchor {
-      color: inherit;
-      text-decoration: none;
-    }
-    .noExpand .arrow {
-      display: none;
-    }
-    .arrow {
-      font-family: Arial Unicode MS, sans-serif;
-    }
-  </ui:style>
-  <g:HTMLPanel addStyleNames='{style.skipBar}'>
-  <div class='{style.text}'>
-    <ui:msg>
-      <g:Anchor ui:field='upArrow' addStyleNames='{style.arrow} {style.anchor}' />
-      <g:Anchor ui:field='skipNum' addStyleNames='{style.anchor}' />
-      <g:Anchor ui:field='downArrow' addStyleNames=' {style.arrow} {style.anchor}' />
-    </ui:msg>
-  </div>
-  </g:HTMLPanel>
-</ui:UiBinder>
\ No newline at end of file
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideSkipManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideSkipManager.java
deleted file mode 100644
index 64f10fc..0000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideSkipManager.java
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// 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.google.gerrit.client.diff;
-
-import com.google.gerrit.client.patches.SkippedLine;
-
-import net.codemirror.lib.CodeMirror;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/** Collapses common regions with {@link SideBySideSkipBar} for {@link SideBySide}. */
-class SideBySideSkipManager extends SkipManager {
-  private SideBySide host;
-
-  SideBySideSkipManager(SideBySide host, SideBySideCommentManager commentManager) {
-    super(commentManager);
-    this.host = host;
-  }
-
-  @Override
-  void render(int context, DiffInfo diff) {
-    List<SkippedLine> skips = getSkippedLines(context, diff);
-
-    if (!skips.isEmpty()) {
-      CodeMirror cmA = host.getCmFromSide(DisplaySide.A);
-      CodeMirror cmB = host.getCmFromSide(DisplaySide.B);
-
-      Set<SkipBar> skipBars = new HashSet<>();
-      setSkipBars(skipBars);
-      for (SkippedLine skip : skips) {
-        SideBySideSkipBar barA = newSkipBar(cmA, DisplaySide.A, skip);
-        SideBySideSkipBar barB = newSkipBar(cmB, DisplaySide.B, skip);
-        SideBySideSkipBar.link(barA, barB);
-        skipBars.add(barA);
-        skipBars.add(barB);
-
-        if (skip.getStartA() == 0 || skip.getStartB() == 0) {
-          barA.upArrow.setVisible(false);
-          barB.upArrow.setVisible(false);
-          setLine0(barB);
-        } else if (skip.getStartA() + skip.getSize() == getLineA()
-            || skip.getStartB() + skip.getSize() == getLineB()) {
-          barA.downArrow.setVisible(false);
-          barB.downArrow.setVisible(false);
-        }
-      }
-    }
-  }
-
-  void remove(SideBySideSkipBar a, SideBySideSkipBar b) {
-    Set<SkipBar> skipBars = getSkipBars();
-    skipBars.remove(a);
-    skipBars.remove(b);
-    if (getLine0() == a || getLine0() == b) {
-      setLine0(null);
-    }
-    if (skipBars.isEmpty()) {
-      setSkipBars(null);
-    }
-  }
-
-  private SideBySideSkipBar newSkipBar(CodeMirror cm, DisplaySide side, SkippedLine skip) {
-    int start = side == DisplaySide.A ? skip.getStartA() : skip.getStartB();
-    int end = start + skip.getSize() - 1;
-
-    SideBySideSkipBar bar = new SideBySideSkipBar(this, cm);
-    host.getDiffTable().add(bar);
-    bar.collapse(start, end, true);
-    return bar;
-  }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.java
index 24aedad..5f86955 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.java
@@ -14,9 +14,205 @@
 
 package com.google.gerrit.client.diff;
 
+import com.google.gerrit.client.patches.PatchUtil;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.resources.client.CssResource;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiHandler;
+import com.google.gwt.user.client.ui.Anchor;
 import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.HTMLPanel;
 
-abstract class SkipBar extends Composite {
-  abstract void expandSideAll();
-  abstract void expandBefore(int cnt);
+import net.codemirror.lib.CodeMirror;
+import net.codemirror.lib.Configuration;
+import net.codemirror.lib.LineWidget;
+import net.codemirror.lib.Pos;
+import net.codemirror.lib.TextMarker;
+import net.codemirror.lib.TextMarker.FromTo;
+
+class SkipBar extends Composite {
+  interface Binder extends UiBinder<HTMLPanel, SkipBar> {}
+  private static final Binder uiBinder = GWT.create(Binder.class);
+  private static final int NUM_ROWS_TO_EXPAND = 10;
+  private static final int UP_DOWN_THRESHOLD = 30;
+
+  interface SkipBarStyle extends CssResource {
+    String noExpand();
+  }
+
+  @UiField(provided = true) Anchor skipNum;
+  @UiField(provided = true) Anchor upArrow;
+  @UiField(provided = true) Anchor downArrow;
+  @UiField SkipBarStyle style;
+
+  private final SkipManager manager;
+  private final CodeMirror cm;
+
+  private LineWidget lineWidget;
+  private TextMarker textMarker;
+  private SkipBar otherBar;
+
+  SkipBar(SkipManager manager, final CodeMirror cm) {
+    this.manager = manager;
+    this.cm = cm;
+
+    skipNum = new Anchor(true);
+    upArrow = new Anchor(true);
+    downArrow = new Anchor(true);
+    initWidget(uiBinder.createAndBindUi(this));
+    addDomHandler(new ClickHandler() {
+      @Override
+      public void onClick(ClickEvent event) {
+        cm.focus();
+      }
+    }, ClickEvent.getType());
+  }
+
+  void collapse(int start, int end, boolean attach) {
+    if (attach) {
+      boolean isNew = lineWidget == null;
+      Configuration cfg = Configuration.create()
+          .set("coverGutter", true)
+          .set("noHScroll", true);
+      if (start == 0) { // First line workaround
+        lineWidget = cm.addLineWidget(end + 1, getElement(), cfg.set("above", true));
+      } else {
+        lineWidget = cm.addLineWidget(start - 1, getElement(), cfg);
+      }
+      if (isNew) {
+        lineWidget.onFirstRedraw(new Runnable() {
+          @Override
+          public void run() {
+            int w = cm.getGutterElement().getOffsetWidth();
+            getElement().getStyle().setPaddingLeft(w, Unit.PX);
+          }
+        });
+      }
+    }
+
+    textMarker = cm.markText(
+        Pos.create(start, 0),
+        Pos.create(end),
+        Configuration.create()
+          .set("collapsed", true)
+          .set("inclusiveLeft", true)
+          .set("inclusiveRight", true));
+
+    textMarker.on("beforeCursorEnter", new Runnable() {
+      @Override
+      public void run() {
+        expandAll();
+      }
+    });
+
+    int skipped = end - start + 1;
+    if (skipped <= UP_DOWN_THRESHOLD) {
+      addStyleName(style.noExpand());
+    } else {
+      upArrow.setHTML(PatchUtil.M.expandBefore(NUM_ROWS_TO_EXPAND));
+      downArrow.setHTML(PatchUtil.M.expandAfter(NUM_ROWS_TO_EXPAND));
+    }
+    skipNum.setText(PatchUtil.M.patchSkipRegion(Integer
+        .toString(skipped)));
+  }
+
+  static void link(SkipBar barA, SkipBar barB) {
+    barA.otherBar = barB;
+    barB.otherBar = barA;
+  }
+
+  private void clearMarkerAndWidget() {
+    textMarker.clear();
+    lineWidget.clear();
+  }
+
+  void expandBefore(int cnt) {
+    expandSideBefore(cnt);
+
+    if (otherBar != null) {
+      otherBar.expandSideBefore(cnt);
+    }
+  }
+
+  private void expandSideBefore(int cnt) {
+    FromTo range = textMarker.find();
+    int oldStart = range.from().line();
+    int newStart = oldStart + cnt;
+    int end = range.to().line();
+    clearMarkerAndWidget();
+    collapse(newStart, end, true);
+    updateSelection();
+  }
+
+  void expandSideAll() {
+    clearMarkerAndWidget();
+    removeFromParent();
+  }
+
+  private void expandAfter() {
+    FromTo range = textMarker.find();
+    int start = range.from().line();
+    int oldEnd = range.to().line();
+    int newEnd = oldEnd - NUM_ROWS_TO_EXPAND;
+    boolean attach = start == 0;
+    if (attach) {
+      clearMarkerAndWidget();
+    } else {
+      textMarker.clear();
+    }
+    collapse(start, newEnd, attach);
+    updateSelection();
+  }
+
+  private void updateSelection() {
+    if (cm.somethingSelected()) {
+      FromTo sel = cm.getSelectedRange();
+      cm.setSelection(sel.from(), sel.to());
+    }
+  }
+
+  @UiHandler("skipNum")
+  void onExpandAll(@SuppressWarnings("unused") ClickEvent e) {
+    expandAll();
+    updateSelection();
+    if (otherBar != null) {
+      otherBar.expandAll();
+      otherBar.updateSelection();
+    }
+    cm.refresh();
+    cm.focus();
+  }
+
+  private void expandAll() {
+    expandSideAll();
+    if (otherBar != null) {
+      otherBar.expandSideAll();
+    }
+    manager.remove(this, otherBar);
+  }
+
+  @UiHandler("upArrow")
+  void onExpandBefore(@SuppressWarnings("unused") ClickEvent e) {
+    expandBefore(NUM_ROWS_TO_EXPAND);
+    if (otherBar != null) {
+      otherBar.expandBefore(NUM_ROWS_TO_EXPAND);
+    }
+    cm.refresh();
+    cm.focus();
+  }
+
+  @UiHandler("downArrow")
+  void onExpandAfter(@SuppressWarnings("unused") ClickEvent e) {
+    expandAfter();
+
+    if (otherBar != null) {
+      otherBar.expandAfter();
+    }
+    cm.refresh();
+    cm.focus();
+  }
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedSkipBar.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.ui.xml
similarity index 94%
rename from gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedSkipBar.ui.xml
rename to gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.ui.xml
index ad05ada..bf3c425 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedSkipBar.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.ui.xml
@@ -16,7 +16,7 @@
 -->
 <ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
     xmlns:g='urn:import:com.google.gwt.user.client.ui'>
-  <ui:style gss='false' type='com.google.gerrit.client.diff.UnifiedSkipBar.SkipBarStyle'>
+  <ui:style gss='false' type='com.google.gerrit.client.diff.SkipBar.SkipBarStyle'>
     .skipBar {
       background-color: #def;
       height: 1.3em;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipManager.java
index 8290d98..cf23694 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipManager.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipManager.java
@@ -19,34 +19,34 @@
 import com.google.gerrit.extensions.client.DiffPreferencesInfo;
 import com.google.gwt.core.client.JsArray;
 
+import net.codemirror.lib.CodeMirror;
+
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
-/** Collapses common regions with {@link SideBySideSkipBar} for {@link SideBySide}
+/** Collapses common regions with {@link SkipBar} for {@link SideBySide}
  *  and {@link Unified}. */
-abstract class SkipManager {
-  private Set<SkipBar> skipBars;
+class SkipManager {
+  private final Set<SkipBar> skipBars;
+  private final DiffScreen host;
   private SkipBar line0;
-  private CommentManager commentManager;
-  private int lineA;
-  private int lineB;
 
-  SkipManager(CommentManager commentManager) {
-    this.commentManager = commentManager;
+  SkipManager(DiffScreen host) {
+    this.host = host;
+    this.skipBars = new HashSet<>();
   }
 
-  abstract void render(int context, DiffInfo diff);
-
-  List<SkippedLine> getSkippedLines(int context, DiffInfo diff) {
+  void render(int context, DiffInfo diff) {
     if (context == DiffPreferencesInfo.WHOLE_FILE_CONTEXT) {
-      return new ArrayList<>();
+      return;
     }
 
-    lineA = 0;
-    lineB = 0;
-    JsArray<Region> regions = diff.content();
     List<SkippedLine> skips = new ArrayList<>();
+    int lineA = 0;
+    int lineB = 0;
+    JsArray<Region> regions = diff.content();
     for (int i = 0; i < regions.length(); i++) {
       Region current = regions.get(i);
       if (current.ab() != null || current.common() || current.skip() > 0) {
@@ -69,7 +69,56 @@
         lineB += current.b() != null ? current.b().length() : 0;
       }
     }
-    return commentManager.splitSkips(context, skips);
+    skips = host.getCommentManager().splitSkips(context, skips);
+    renderSkips(skips, lineA, lineB);
+  }
+
+  private void renderSkips(List<SkippedLine> skips, int lineA, int lineB) {
+    if (!skips.isEmpty()) {
+      boolean isSideBySide = host.isSideBySide();
+      CodeMirror cmA = null;
+      if (isSideBySide) {
+        cmA = host.getCmFromSide(DisplaySide.A);
+      }
+      CodeMirror cmB = host.getCmFromSide(DisplaySide.B);
+
+      for (SkippedLine skip : skips) {
+        SkipBar barA = null;
+        SkipBar barB = newSkipBar(cmB, DisplaySide.B, skip);
+        skipBars.add(barB);
+        if (isSideBySide) {
+          barA = newSkipBar(cmA, DisplaySide.A, skip);
+          SkipBar.link(barA, barB);
+          skipBars.add(barA);
+        }
+
+        if (skip.getStartA() == 0 || skip.getStartB() == 0) {
+          if (isSideBySide) {
+            barA.upArrow.setVisible(false);
+          }
+          barB.upArrow.setVisible(false);
+          setLine0(barB);
+        } else if (skip.getStartA() + skip.getSize() == lineA
+            || skip.getStartB() + skip.getSize() == lineB) {
+          if (isSideBySide) {
+            barA.downArrow.setVisible(false);
+          }
+          barB.downArrow.setVisible(false);
+        }
+      }
+    }
+  }
+
+  private SkipBar newSkipBar(CodeMirror cm, DisplaySide side,
+      SkippedLine skip) {
+    int start = host.getCmLine(
+        side == DisplaySide.A ? skip.getStartA() : skip.getStartB(), side);
+    int end = start + skip.getSize() - 1;
+
+    SkipBar bar = new SkipBar(this, cm);
+    host.getDiffTable().add(bar);
+    bar.collapse(start, end, true);
+    return bar;
   }
 
   void ensureFirstLineIsVisible() {
@@ -80,36 +129,27 @@
   }
 
   void removeAll() {
-    if (skipBars != null) {
+    if (!skipBars.isEmpty()) {
       for (SkipBar bar : skipBars) {
         bar.expandSideAll();
       }
-      skipBars = null;
       line0 = null;
     }
   }
 
+  void remove(SkipBar a, SkipBar b) {
+    skipBars.remove(a);
+    skipBars.remove(b);
+    if (getLine0() == a || getLine0() == b) {
+      setLine0(null);
+    }
+  }
+
   SkipBar getLine0() {
     return line0;
   }
 
-  int getLineA() {
-    return lineA;
-  }
-
-  int getLineB() {
-    return lineB;
-  }
-
   void setLine0(SkipBar bar) {
     line0 = bar;
   }
-
-  void setSkipBars(Set<SkipBar> bars) {
-    skipBars = bars;
-  }
-
-  Set<SkipBar> getSkipBars() {
-    return skipBars;
-  }
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Unified.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Unified.java
index 6b48f15..9b04d6f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Unified.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Unified.java
@@ -68,7 +68,6 @@
 
   private UnifiedChunkManager chunkManager;
   private UnifiedCommentManager commentManager;
-  private UnifiedSkipManager skipManager;
 
   private boolean autoHideDiffTableHeader;
 
@@ -120,12 +119,12 @@
     if (getStartLine() == 0) {
       DiffChunkInfo d = chunkManager.getFirst();
       if (d != null) {
-        if (d.isEdit() && d.getSide() == DisplaySide.A) {
+        if (d.edit && d.side == DisplaySide.A) {
           setStartSide(DisplaySide.B);
         } else {
-          setStartSide(d.getSide());
+          setStartSide(d.side);
         }
-        setStartLine(chunkManager.getCmLine(d.getStart(), d.getSide()) + 1);
+        setStartLine(chunkManager.getCmLine(d.start, d.side) + 1);
       }
     }
     if (getStartSide() != null && getStartLine() > 0) {
@@ -188,7 +187,6 @@
     setShowTabs(prefs.showTabs());
 
     chunkManager = new UnifiedChunkManager(this, cm, diffTable.scrollbar);
-    skipManager = new UnifiedSkipManager(this, commentManager);
 
     operation(new Runnable() {
       @Override
@@ -407,7 +405,7 @@
   }
 
   @Override
-  UnifiedSkipManager getSkipManager() {
-    return skipManager;
+  boolean isSideBySide() {
+    return false;
   }
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedChunkManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedChunkManager.java
index 1e6ed0c..13647d6 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedChunkManager.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedChunkManager.java
@@ -83,29 +83,27 @@
   void render(DiffInfo diff) {
     super.render();
 
-    LineMapper mapper = getLineMapper();
-
     chunks = new ArrayList<>();
 
     int cmLine = 0;
     boolean useIntralineBg = diff.metaA() == null || diff.metaB() == null;
 
     for (Region current : Natives.asList(diff.content())) {
-      int origLineA = mapper.getLineA();
-      int origLineB = mapper.getLineB();
+      int origLineA = lineMapper.getLineA();
+      int origLineB = lineMapper.getLineB();
       if (current.ab() != null) {
         int length = current.ab().length();
-        mapper.appendCommon(length);
+        lineMapper.appendCommon(length);
         for (int i = 0; i < length; i++) {
           host.setLineNumber(DisplaySide.A, cmLine + i, origLineA + i + 1);
           host.setLineNumber(DisplaySide.B, cmLine + i, origLineB + i + 1);
         }
         cmLine += length;
       } else if (current.skip() > 0) {
-        mapper.appendCommon(current.skip());
+        lineMapper.appendCommon(current.skip());
         cmLine += current.skip(); // Maybe current.ab().length();
       } else if (current.common()) {
-        mapper.appendCommon(current.b().length());
+        lineMapper.appendCommon(current.b().length());
         cmLine += current.b().length();
       } else {
         cmLine += render(current, cmLine, useIntralineBg);
@@ -114,10 +112,8 @@
   }
 
   private int render(Region region, int cmLine, boolean useIntralineBg) {
-    LineMapper mapper = getLineMapper();
-
-    int startA = mapper.getLineA();
-    int startB = mapper.getLineB();
+    int startA = lineMapper.getLineA();
+    int startB = lineMapper.getLineB();
 
     JsArrayString a = region.a();
     JsArrayString b = region.b();
@@ -137,18 +133,18 @@
     markEdit(DisplaySide.A, cmLine, a, region.editA());
     markEdit(DisplaySide.B, cmLine + aLen, b, region.editB());
     addGutterTag(region, cmLine); // TODO: verify addGutterTag
-    mapper.appendReplace(aLen, bLen);
+    lineMapper.appendReplace(aLen, bLen);
 
-    int endA = mapper.getLineA() - 1;
-    int endB = mapper.getLineB() - 1;
+    int endA = lineMapper.getLineA() - 1;
+    int endB = lineMapper.getLineB() - 1;
     if (aLen > 0) {
-      addDiffChunk(DisplaySide.A, endA, aLen, cmLine, bLen > 0);
+      addDiffChunk(DisplaySide.A, endA, endB, aLen, cmLine, bLen > 0);
       for (int j = 0; j < aLen; j++) {
         host.setLineNumber(DisplaySide.A, cmLine + j, startA + j + 1);
       }
     }
     if (bLen > 0) {
-      addDiffChunk(DisplaySide.B, endB, bLen, cmLine + aLen, aLen > 0);
+      addDiffChunk(DisplaySide.B, endB, endA, bLen, cmLine + aLen, aLen > 0);
       for (int j = 0; j < bLen; j++) {
         host.setLineNumber(DisplaySide.B, cmLine + aLen + j, startB + j + 1);
       }
@@ -157,7 +153,6 @@
   }
 
   private void addGutterTag(Region region, int cmLine) {
-    Scrollbar scrollbar = getScrollbar();
     if (region.a() == null) {
       scrollbar.insert(cm, cmLine, region.b().length());
     } else if (region.b() == null) {
@@ -209,10 +204,10 @@
         : UnifiedTable.style.diffInsert();
   }
 
-  private void addDiffChunk(DisplaySide side, int chunkEnd, int chunkSize,
-      int cmLine, boolean edit) {
-    chunks.add(new UnifiedDiffChunkInfo(side, chunkEnd - chunkSize + 1, chunkEnd,
-        cmLine, edit));
+  private void addDiffChunk(DisplaySide side, int chunkEnd, int otherChunkEnd,
+      int chunkSize, int cmLine, boolean edit) {
+    chunks.add(new UnifiedDiffChunkInfo(side, chunkEnd - chunkSize + 1,
+        otherChunkEnd - chunkSize + 1, chunkEnd, cmLine, edit));
   }
 
   @Override
@@ -225,7 +220,7 @@
             : 0;
         int res = Collections.binarySearch(
                 chunks,
-                new UnifiedDiffChunkInfo(cm.side(), 0, 0, line, false),
+                new UnifiedDiffChunkInfo(cm.side(), 0, 0, 0, line, false),
                 getDiffChunkComparatorCmLine());
         diffChunkNavHelper(chunks, host, res, dir);
       }
@@ -237,7 +232,7 @@
     return new Comparator<UnifiedDiffChunkInfo>() {
       @Override
       public int compare(UnifiedDiffChunkInfo o1, UnifiedDiffChunkInfo o2) {
-        return o1.getCmLine() - o2.getCmLine();
+        return o1.cmLine - o2.cmLine;
       }
     };
   }
@@ -247,31 +242,30 @@
     int res =
         Collections.binarySearch(chunks,
             new UnifiedDiffChunkInfo(
-                side, line, 0, 0, false), // Dummy DiffChunkInfo
-            getDiffChunkComparator());
+                side, line, 0, 0, 0, false)); // Dummy DiffChunkInfo
     if (res >= 0) {
-      return chunks.get(res).getCmLine();
+      return chunks.get(res).cmLine;
     } else { // The line might be within a DiffChunk
       res = -res - 1;
       if (res > 0) {
         UnifiedDiffChunkInfo info = chunks.get(res - 1);
-        if (side == DisplaySide.A && info.isEdit()
-            && info.getSide() == DisplaySide.B) {
+        if (side == DisplaySide.A && info.edit
+            && info.side == DisplaySide.B) {
           // Need to use the start and cmLine of the deletion chunk
           UnifiedDiffChunkInfo delete = chunks.get(res - 2);
-          if (line <= delete.getEnd()) {
-            return delete.getCmLine() + line - delete.getStart();
+          if (line <= delete.end) {
+            return delete.cmLine + line - delete.start;
           } else {
             // Need to add the length of the insertion chunk
-            return delete.getCmLine() + line - delete.getStart()
-                + info.getEnd() - info.getStart() + 1;
+            return delete.cmLine + line - delete.start
+                + info.end - info.start + 1;
           }
-        } else if (side == info.getSide()) {
-          return info.getCmLine() + line - info.getStart();
+        } else if (side == info.side) {
+          return info.cmLine + line - info.start;
         } else {
-          return info.getCmLine()
-              + getLineMapper().lineOnOther(side, line).getLine()
-              - info.getStart();
+          return info.cmLine
+              + lineMapper.lineOnOther(side, line).getLine()
+              - info.start;
         }
       } else {
         return line;
@@ -283,30 +277,30 @@
     int res =
         Collections.binarySearch(chunks,
             new UnifiedDiffChunkInfo(
-                DisplaySide.A, 0, 0, cmLine, false), // Dummy DiffChunkInfo
+                DisplaySide.A, 0, 0, 0, cmLine, false), // Dummy DiffChunkInfo
             getDiffChunkComparatorCmLine());
     if (res >= 0) {  // The line is right at the start of a diff chunk.
       UnifiedDiffChunkInfo info = chunks.get(res);
       return new LineRegionInfo(
-          info.getStart(), displaySideToRegionType(info.getSide()));
+          info.start, displaySideToRegionType(info.side));
     } else {  // The line might be within or after a diff chunk.
       res = -res - 1;
       if (res > 0) {
         UnifiedDiffChunkInfo info = chunks.get(res - 1);
-        int lineOnInfoSide = info.getStart() + cmLine - info.getCmLine();
-        if (lineOnInfoSide > info.getEnd()) { // After a diff chunk
-          if (info.getSide() == DisplaySide.A) {
+        int lineOnInfoSide = info.start + cmLine - info.cmLine;
+        if (lineOnInfoSide > info.end) { // After a diff chunk
+          if (info.side == DisplaySide.A) {
             // For the common region after a deletion chunk, associate the line
             // on side B with a common region.
             return new LineRegionInfo(
-                getLineMapper().lineOnOther(DisplaySide.A, lineOnInfoSide)
+                lineMapper.lineOnOther(DisplaySide.A, lineOnInfoSide)
                     .getLine(), RegionType.COMMON);
           } else {
             return new LineRegionInfo(lineOnInfoSide, RegionType.COMMON);
           }
         } else { // Within a diff chunk
           return new LineRegionInfo(
-              lineOnInfoSide, displaySideToRegionType(info.getSide()));
+              lineOnInfoSide, displaySideToRegionType(info.side));
         }
       } else {
         // The line is before any diff chunk, so it always equals cmLine and
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedCommentManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedCommentManager.java
index 411b64f..9fae92c 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedCommentManager.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedCommentManager.java
@@ -170,7 +170,7 @@
   @Override
   void insertNewDraft(DisplaySide side, int cmLinePlusOne) {
     if (cmLinePlusOne == 0) {
-      getDiffScreen().getSkipManager().ensureFirstLineIsVisible();
+      getDiffScreen().skipManager.ensureFirstLineIsVisible();
     }
 
     CommentGroup group = group(side, cmLinePlusOne);
@@ -354,7 +354,7 @@
       if ((fromInfo.type == RegionType.INSERT
           || fromInfo.type == RegionType.COMMON)
           && toInfo.type == RegionType.DELETE) {
-        LineOnOtherInfo infoOnSideA = manager.getLineMapper()
+        LineOnOtherInfo infoOnSideA = manager.lineMapper
             .lineOnOther(DisplaySide.B, fromInfo.line);
         int startLineOnSideA = infoOnSideA.getLine();
         if (infoOnSideA.isAligned()) {
@@ -366,7 +366,7 @@
         to.line(toInfo.line);
       } else if (fromInfo.type == RegionType.DELETE
           && toInfo.type == RegionType.INSERT) {
-        LineOnOtherInfo infoOnSideB = manager.getLineMapper()
+        LineOnOtherInfo infoOnSideB = manager.lineMapper
             .lineOnOther(DisplaySide.A, fromInfo.line);
         int startLineOnSideB = infoOnSideB.getLine();
         if (infoOnSideB.isAligned()) {
@@ -378,7 +378,7 @@
         to.line(toInfo.line);
       } else if (fromInfo.type == RegionType.DELETE
           && toInfo.type == RegionType.COMMON) {
-        int toLineOnSideA = manager.getLineMapper()
+        int toLineOnSideA = manager.lineMapper
             .lineOnOther(DisplaySide.B, toInfo.line).getLine();
         from.line(fromInfo.line);
         // Force the end line to be on the same side as the start line.
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedDiffChunkInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedDiffChunkInfo.java
index 844be78..542a153 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedDiffChunkInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedDiffChunkInfo.java
@@ -16,15 +16,11 @@
 
 public class UnifiedDiffChunkInfo extends DiffChunkInfo {
 
-  private int cmLine;
+  final int cmLine;
 
-  UnifiedDiffChunkInfo(DisplaySide side,
-      int start, int end, int cmLine, boolean edit) {
-    super(side, start, end, edit);
+  UnifiedDiffChunkInfo(DisplaySide side, int start, int startOnOther, int end,
+      int cmLine, boolean edit) {
+    super(side, start, startOnOther, end, edit);
     this.cmLine = cmLine;
   }
-
-  int getCmLine() {
-    return cmLine;
-  }
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedSkipBar.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedSkipBar.java
deleted file mode 100644
index 2c26774..0000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedSkipBar.java
+++ /dev/null
@@ -1,193 +0,0 @@
-//Copyright (C) 2013 The Android Open Source Project
-//
-// 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.google.gerrit.client.diff;
-
-import com.google.gerrit.client.patches.PatchUtil;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Style.Unit;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.resources.client.CssResource;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.HTMLPanel;
-
-import net.codemirror.lib.CodeMirror;
-import net.codemirror.lib.Configuration;
-import net.codemirror.lib.LineWidget;
-import net.codemirror.lib.Pos;
-import net.codemirror.lib.TextMarker;
-import net.codemirror.lib.TextMarker.FromTo;
-
-/** The Widget that handles expanding of skipped lines */
-class UnifiedSkipBar extends SkipBar {
-  interface Binder extends UiBinder<HTMLPanel, UnifiedSkipBar> {}
-  private static final Binder uiBinder = GWT.create(Binder.class);
-  private static final int NUM_ROWS_TO_EXPAND = 10;
-  private static final int UP_DOWN_THRESHOLD = 30;
-
-  interface SkipBarStyle extends CssResource {
-    String noExpand();
-  }
-
-  @UiField(provided = true) Anchor skipNum;
-  @UiField(provided = true) Anchor upArrow;
-  @UiField(provided = true) Anchor downArrow;
-  @UiField SkipBarStyle style;
-
-  private final UnifiedSkipManager manager;
-  private final CodeMirror cm;
-
-  private LineWidget lineWidget;
-  private TextMarker textMarker;
-
-  UnifiedSkipBar(UnifiedSkipManager manager, final CodeMirror cm) {
-    this.manager = manager;
-    this.cm = cm;
-
-    skipNum = new Anchor(true);
-    upArrow = new Anchor(true);
-    downArrow = new Anchor(true);
-    initWidget(uiBinder.createAndBindUi(this));
-    addDomHandler(new ClickHandler() {
-      @Override
-      public void onClick(ClickEvent event) {
-        cm.focus();
-      }
-    }, ClickEvent.getType());
-  }
-
-  void collapse(int start, int end, boolean attach) {
-    if (attach) {
-      boolean isNew = lineWidget == null;
-      Configuration cfg = Configuration.create()
-          .set("coverGutter", true)
-          .set("noHScroll", true);
-      if (start == 0) { // First line workaround
-        lineWidget = cm.addLineWidget(end + 1, getElement(), cfg.set("above", true));
-      } else {
-        lineWidget = cm.addLineWidget(start - 1, getElement(), cfg);
-      }
-      if (isNew) {
-        lineWidget.onFirstRedraw(new Runnable() {
-          @Override
-          public void run() {
-            int w = cm.getGutterElement().getOffsetWidth();
-            getElement().getStyle().setPaddingLeft(w, Unit.PX);
-          }
-        });
-      }
-    }
-
-    textMarker = cm.markText(
-        Pos.create(start, 0),
-        Pos.create(end),
-        Configuration.create()
-          .set("collapsed", true)
-          .set("inclusiveLeft", true)
-          .set("inclusiveRight", true));
-
-    textMarker.on("beforeCursorEnter", new Runnable() {
-      @Override
-      public void run() {
-        expandAll();
-      }
-    });
-
-    int skipped = end - start + 1;
-    if (skipped <= UP_DOWN_THRESHOLD) {
-      addStyleName(style.noExpand());
-    } else {
-      upArrow.setHTML(PatchUtil.M.expandBefore(NUM_ROWS_TO_EXPAND));
-      downArrow.setHTML(PatchUtil.M.expandAfter(NUM_ROWS_TO_EXPAND));
-    }
-    skipNum.setText(PatchUtil.M.patchSkipRegion(Integer
-        .toString(skipped)));
-  }
-
-  private void clearMarkerAndWidget() {
-    textMarker.clear();
-    lineWidget.clear();
-  }
-
-  @Override
-  void expandBefore(int cnt) {
-    expandSideBefore(cnt);
-  }
-
-  private void expandSideBefore(int cnt) {
-    FromTo range = textMarker.find();
-    int oldStart = range.from().line();
-    int newStart = oldStart + cnt;
-    int end = range.to().line();
-    clearMarkerAndWidget();
-    collapse(newStart, end, true);
-    updateSelection();
-  }
-
-  @Override
-  void expandSideAll() {
-    clearMarkerAndWidget();
-    removeFromParent();
-  }
-
-  private void expandAfter() {
-    FromTo range = textMarker.find();
-    int start = range.from().line();
-    int oldEnd = range.to().line();
-    int newEnd = oldEnd - NUM_ROWS_TO_EXPAND;
-    boolean attach = start == 0;
-    if (attach) {
-      clearMarkerAndWidget();
-    } else {
-      textMarker.clear();
-    }
-    collapse(start, newEnd, attach);
-    updateSelection();
-  }
-
-  private void updateSelection() {
-    if (cm.somethingSelected()) {
-      FromTo sel = cm.getSelectedRange();
-      cm.setSelection(sel.from(), sel.to());
-    }
-  }
-
-  @UiHandler("skipNum")
-  void onExpandAll(@SuppressWarnings("unused") ClickEvent e) {
-    expandAll();
-    updateSelection();
-    cm.focus();
-  }
-
-  private void expandAll() {
-    expandSideAll();
-    manager.remove(this);
-  }
-
-  @UiHandler("upArrow")
-  void onExpandBefore(@SuppressWarnings("unused") ClickEvent e) {
-    expandBefore(NUM_ROWS_TO_EXPAND);
-    cm.focus();
-  }
-
-  @UiHandler("downArrow")
-  void onExpandAfter(@SuppressWarnings("unused") ClickEvent e) {
-    expandAfter();
-    cm.focus();
-  }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedSkipManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedSkipManager.java
deleted file mode 100644
index 7554a87..0000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedSkipManager.java
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// 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.google.gerrit.client.diff;
-
-import com.google.gerrit.client.patches.SkippedLine;
-
-import net.codemirror.lib.CodeMirror;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/** Collapses common regions with {@link UnifiedSkipBar} for {@link Unified}. */
-class UnifiedSkipManager extends SkipManager {
-  private Unified host;
-
-  UnifiedSkipManager(Unified host, UnifiedCommentManager commentManager) {
-    super(commentManager);
-    this.host = host;
-  }
-
-  @Override
-  void render(int context, DiffInfo diff) {
-    List<SkippedLine> skips = getSkippedLines(context, diff);
-
-    if (!skips.isEmpty()) {
-      CodeMirror cm = host.getCm();
-
-      Set<SkipBar> skipBars = new HashSet<>();
-      setSkipBars(skipBars);
-      for (SkippedLine skip : skips) {
-        UnifiedSkipBar bar = newSkipBar(cm, skip);
-        skipBars.add(bar);
-
-        if (skip.getStartA() == 0 || skip.getStartB() == 0) {
-          bar.upArrow.setVisible(false);
-          setLine0(bar);
-        } else if (skip.getStartA() + skip.getSize() == getLineA()
-            || skip.getStartB() + skip.getSize() == getLineB()) {
-          bar.downArrow.setVisible(false);
-        }
-      }
-    }
-  }
-
-  void remove(UnifiedSkipBar bar) {
-    Set<SkipBar> skipBars = getSkipBars();
-    skipBars.remove(bar);
-    if (getLine0() == bar) {
-      setLine0(null);
-    }
-    if (skipBars.isEmpty()) {
-      setSkipBars(null);
-    }
-  }
-
-  private UnifiedSkipBar newSkipBar(CodeMirror cm, SkippedLine skip) {
-    int start = host.getCmLine(skip.getStartA(), DisplaySide.A);
-    int end = start + skip.getSize() - 1;
-
-    UnifiedSkipBar bar = new UnifiedSkipBar(this, cm);
-    host.getDiffTable().add(bar);
-    bar.collapse(start, end, true);
-    return bar;
-  }
-}