Merge "Reduce REST API calls on ProjectListScreen"
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 47d074f..a95a1fa 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -3387,6 +3387,7 @@
 |===========================
 |Field Name                ||Description
 |`change_id`               |optional|The Change-Id of the change.
+|`status`                  |optional|The status of the change.
 |`commit`                  ||The commit as a
 link:#commit-info[CommitInfo] entity.
 |`_change_number`          |optional|The change number.
diff --git a/Documentation/user-review-ui.txt b/Documentation/user-review-ui.txt
index 0cea546..d303703 100644
--- a/Documentation/user-review-ui.txt
+++ b/Documentation/user-review-ui.txt
@@ -538,7 +538,7 @@
 note that following the link to an indirect descendant change may
 result in a completely different related changes listing.
 
-** [[closed-ancestor]]Black Dot:
+** [[merged-ancestor]]Black Dot:
 +
 Indicates a merged ancestor, e.g. the commit was directly pushed into
 the repository bypassing code review, or the ancestor change was
@@ -547,6 +547,10 @@
 the commit was done on `branch-a`, but was then pushed to
 `refs/for/branch-b`.
 
+** [[abandoned-change]]Dark Red Dot:
++
+Indicates an abandoned change.
+
 +
 image::images/user-review-ui-change-screen-related-changes-indicators.png[width=800, link="images/user-review-ui-change-screen-related-changes-indicators.png"]
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java
index 5ba520c..64025db 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java
@@ -23,7 +23,7 @@
   String loadingPlugins();
 
   String signInDialogTitle();
-  String signInDialogClose();
+  String signInDialogGoAnonymous();
 
   String linkIdentityDialogTitle();
   String registerDialogTitle();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties
index 58a6d08..a335d6b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties
@@ -4,7 +4,7 @@
 loadingPlugins = Loading plugins ...
 
 signInDialogTitle = Code Review - Sign In
-signInDialogClose = Close
+signInDialogGoAnonymous = Go Anonymous
 
 linkIdentityDialogTitle = Code Review - Link Identity
 registerDialogTitle = Code Review - Register New Account
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/NotSignedInDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/NotSignedInDialog.java
index 6a417ca..83c32cd 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/NotSignedInDialog.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/NotSignedInDialog.java
@@ -19,23 +19,25 @@
 import com.google.gwt.event.logical.shared.CloseEvent;
 import com.google.gwt.event.logical.shared.CloseHandler;
 import com.google.gwt.user.client.History;
+import com.google.gwt.user.client.Window;
 import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.FlowPanel;
 import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.Label;
 import com.google.gwt.user.client.ui.PopupPanel;
 import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.user.client.AutoCenterDialogBox;
+import com.google.gwtexpui.user.client.PluginSafePopupPanel;
 
 /** A dialog box telling the user they are not signed in. */
-public class NotSignedInDialog extends AutoCenterDialogBox implements CloseHandler<PopupPanel> {
-
+public class NotSignedInDialog extends PluginSafePopupPanel implements CloseHandler<PopupPanel> {
   private Button signin;
-  private boolean buttonClicked = false;
+  private boolean buttonClicked;
 
   public NotSignedInDialog() {
     super(/* auto hide */false, /* modal */true);
     setGlassEnabled(true);
-    setText(Gerrit.C.notSignedInTitle());
+    getGlassElement().addClassName(Gerrit.RESOURCES.css().errorDialogGlass());
+    addStyleName(Gerrit.RESOURCES.css().errorDialog());
 
     final FlowPanel buttons = new FlowPanel();
     signin = new Button();
@@ -52,7 +54,7 @@
 
     final Button close = new Button();
     close.getElement().getStyle().setProperty("marginLeft", "200px");
-    close.setText(Gerrit.C.signInDialogClose());
+    close.setText(Gerrit.C.signInDialogGoAnonymous());
     close.addClickHandler(new ClickHandler() {
       @Override
       public void onClick(ClickEvent event) {
@@ -63,13 +65,18 @@
     });
     buttons.add(close);
 
-    final FlowPanel center = new FlowPanel();
+    Label title = new Label(Gerrit.C.notSignedInTitle());
+    title.setStyleName(Gerrit.RESOURCES.css().errorDialogTitle());
+
+    FlowPanel center = new FlowPanel();
+    center.add(title);
     center.add(new HTML(Gerrit.C.notSignedInBody()));
     center.add(buttons);
     add(center);
 
-    center.setWidth("400px");
-
+    int l = Window.getScrollLeft() + 20;
+    int t = Window.getScrollTop() + 20;
+    setPopupPosition(l, t);
     addCloseHandler(this);
   }
 
@@ -84,7 +91,7 @@
 
   @Override
   public void center() {
-    super.center();
+    show();
     GlobalKey.dialog(this);
     signin.setFocus(true);
   }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java
index db1acbf..6fb2fe0 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java
@@ -409,7 +409,7 @@
     }
 
     AccountApi.self().view("preferences")
-        .post(Preferences.create(p, items), new GerritCallback<Preferences>() {
+        .put(Preferences.create(p, items), new GerritCallback<Preferences>() {
           @Override
           public void onSuccess(Preferences prefs) {
             Gerrit.getUserAccount().setGeneralPreferences(p);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.java
index ada960c..a7d4945 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.java
@@ -47,4 +47,6 @@
   String sameTopicTooltip();
   String noChanges();
   String indirectAncestor();
+  String merged();
+  String abandoned();
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.properties
index 172c1de..8852d4e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.properties
@@ -28,3 +28,5 @@
 sameTopicTooltip = Changes with the same topic
 noChanges = No Changes
 indirectAncestor = Indirect ancestor
+merged = Merged
+abandoned = Abandoned
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java
index 96cbc28..d7a44ed 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java
@@ -270,7 +270,7 @@
       PreferenceInput in = PreferenceInput.create();
       in.download_scheme(scheme);
       AccountApi.self().view("preferences")
-          .post(in, new AsyncCallback<JavaScriptObject>() {
+          .put(in, new AsyncCallback<JavaScriptObject>() {
             @Override
             public void onSuccess(JavaScriptObject result) {
             }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Message.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Message.java
index 8c23458..22d39a7 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Message.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Message.java
@@ -146,10 +146,11 @@
   }
 
   private static String elide(final String s, final int len) {
-    if (s == null || s.length() < len || len <= 10) {
+    if (s == null || s.length() <= len || len <= 10) {
       return s;
     }
-    return s.substring(0, len - 10) + "..." + s.substring(s.length() - 10);
+    int i = (len - 3) / 2;
+    return s.substring(0, i) + "..." + s.substring(s.length() - i);
   }
 
   void autoOpen() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/NewChangeScreenBar.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/NewChangeScreenBar.java
index 165ea68..de6eefe 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/NewChangeScreenBar.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/NewChangeScreenBar.java
@@ -84,7 +84,7 @@
 
       Prefs in = Prefs.createObject().cast();
       in.change_screen(sel.name());
-      AccountApi.self().view("preferences").background().post(in,
+      AccountApi.self().view("preferences").background().put(in,
         new AsyncCallback<JavaScriptObject>() {
           @Override public void onFailure(Throwable caught) {}
           @Override public void onSuccess(JavaScriptObject result) {}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChanges.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChanges.java
index 7fbbfc7..5f6c6de 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChanges.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChanges.java
@@ -55,6 +55,8 @@
     String current();
     String gitweb();
     String indirect();
+    String abandoned();
+    String merged();
     String notCurrent();
     String pointer();
     String row();
@@ -342,6 +344,13 @@
     }
 
     public final native String id() /*-{ return this.change_id }-*/;
+
+    public final Change.Status status() {
+      String s = statusRaw();
+      return s != null ? Change.Status.valueOf(s) : null;
+    }
+    private final native String statusRaw() /*-{ return this.status; }-*/;
+
     public final native CommitInfo commit() /*-{ return this.commit }-*/;
     final native String branch() /*-{ return this.branch }-*/;
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChangesTab.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChangesTab.java
index 0f2ee81..2dfd53e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChangesTab.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChangesTab.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.client.changes.ChangeInfo.CommitInfo;
 import com.google.gerrit.client.changes.Util;
 import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.client.JsArray;
@@ -301,6 +302,14 @@
         sb.setStyleName(RelatedChanges.R.css().notCurrent());
         sb.setAttribute("title", Util.C.notCurrent());
         sb.append('\u25CF');
+      } else if (Change.Status.MERGED == info.status()) {
+        sb.setStyleName(RelatedChanges.R.css().merged());
+        sb.setAttribute("title", Resources.C.merged());
+        sb.append('\u25CF');
+      } else if (Change.Status.ABANDONED == info.status()) {
+        sb.setStyleName(RelatedChanges.R.css().abandoned());
+        sb.setAttribute("title", Resources.C.abandoned());
+        sb.append('\u25CF');
       } else {
         sb.setStyleName(RelatedChanges.R.css().current());
       }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/related_changes.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/related_changes.css
index 2e62b98..4d103ae 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/related_changes.css
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/related_changes.css
@@ -68,6 +68,8 @@
 .current,
 .gitweb,
 .indirect,
+.abandoned,
+.merged,
 .notCurrent {
   display: inline-block;
   text-align: center;
@@ -75,6 +77,7 @@
   width: 12px;
 }
 
+.merged,
 .gitweb {
   color: #000;
 }
@@ -84,6 +87,10 @@
   font-weight: bold;
 }
 
+.abandoned {
+  color: #900;      /* dark red */
+}
+
 .notCurrent {
   color: #FFA62F;   /* orange */
 }
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 6bbb2dd..91582b2 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
@@ -16,9 +16,6 @@
 
 import static com.google.gerrit.client.diff.DisplaySide.A;
 import static com.google.gerrit.client.diff.DisplaySide.B;
-import static com.google.gerrit.client.diff.OverviewBar.MarkType.DELETE;
-import static com.google.gerrit.client.diff.OverviewBar.MarkType.EDIT;
-import static com.google.gerrit.client.diff.OverviewBar.MarkType.INSERT;
 
 import com.google.gerrit.client.diff.DiffInfo.Region;
 import com.google.gerrit.client.diff.DiffInfo.Span;
@@ -35,7 +32,7 @@
 import net.codemirror.lib.CodeMirror;
 import net.codemirror.lib.CodeMirror.LineClassWhere;
 import net.codemirror.lib.Configuration;
-import net.codemirror.lib.LineCharacter;
+import net.codemirror.lib.Pos;
 import net.codemirror.lib.LineWidget;
 import net.codemirror.lib.TextMarker;
 
@@ -79,7 +76,7 @@
   private final SideBySide2 host;
   private final CodeMirror cmA;
   private final CodeMirror cmB;
-  private final OverviewBar sidePanel;
+  private final Scrollbar scrollbar;
   private final LineMapper mapper;
 
   private List<DiffChunkInfo> chunks;
@@ -91,11 +88,11 @@
   ChunkManager(SideBySide2 host,
       CodeMirror cmA,
       CodeMirror cmB,
-      OverviewBar sidePanel) {
+      Scrollbar scrollbar) {
     this.host = host;
     this.cmA = cmA;
     this.cmB = cmB;
-    this.sidePanel = sidePanel;
+    this.scrollbar = scrollbar;
     this.mapper = new LineMapper();
   }
 
@@ -197,11 +194,11 @@
 
   private void addGutterTag(Region region, int startA, int startB) {
     if (region.a() == null) {
-      sidePanel.add(cmB, startB, region.b().length(), INSERT);
+      scrollbar.insert(cmB, startB, region.b().length());
     } else if (region.b() == null) {
-      sidePanel.add(cmA, startA, region.a().length(), DELETE);
+      scrollbar.delete(cmA, cmB, startA, region.a().length());
     } else {
-      sidePanel.add(cmB, startB, region.b().length(), EDIT);
+      scrollbar.edit(cmB, startB, region.b().length());
     }
   }
 
@@ -220,20 +217,20 @@
         .set("className", DiffTable.style.diff())
         .set("readOnly", true);
 
-    LineCharacter last = CodeMirror.pos(0, 0);
+    Pos last = Pos.create(0, 0);
     for (Span span : Natives.asList(edits)) {
-      LineCharacter from = iter.advance(span.skip());
-      LineCharacter to = iter.advance(span.mark());
-      if (from.getLine() == last.getLine()) {
+      Pos from = iter.advance(span.skip());
+      Pos to = iter.advance(span.mark());
+      if (from.line() == last.line()) {
         markers.add(cm.markText(last, from, bg));
       } else {
-        markers.add(cm.markText(CodeMirror.pos(from.getLine(), 0), from, bg));
+        markers.add(cm.markText(Pos.create(from.line(), 0), from, bg));
       }
       markers.add(cm.markText(from, to, diff));
       last = to;
       colorLines(cm, LineClassWhere.BACKGROUND,
           DiffTable.style.diff(),
-          from.getLine(), to.getLine());
+          from.line(), to.line());
     }
   }
 
@@ -294,7 +291,7 @@
     return new Runnable() {
       @Override
       public void run() {
-        int line = cm.hasActiveLine() ? cm.getLineNumber(cm.getActiveLine()) : 0;
+        int line = cm.hasActiveLine() ? cm.getLineNumber(cm.activeLine()) : 0;
         int res = Collections.binarySearch(
                 chunks,
                 new DiffChunkInfo(cm.side(), line, 0, false),
@@ -318,11 +315,11 @@
 
         DiffChunkInfo target = chunks.get(res);
         CodeMirror targetCm = host.getCmFromSide(target.getSide());
-        targetCm.setCursor(LineCharacter.create(target.getStart()));
+        targetCm.setCursor(Pos.create(target.getStart()));
         targetCm.focus();
         targetCm.scrollToY(
             targetCm.heightAtLine(target.getStart(), "local") -
-            0.5 * cmB.getScrollbarV().getClientHeight());
+            0.5 * cmB.scrollbarV().getClientHeight());
       }
     };
   }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBoxUi.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBox.css
similarity index 100%
rename from gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBoxUi.css
rename to gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBox.css
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBox.java
index 1d74520..ca56bf4 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBox.java
@@ -19,6 +19,7 @@
 import com.google.gwt.event.dom.client.MouseOutHandler;
 import com.google.gwt.event.dom.client.MouseOverEvent;
 import com.google.gwt.event.dom.client.MouseOverHandler;
+import com.google.gwt.resources.client.CssResource;
 import com.google.gwt.user.client.ui.Composite;
 
 import net.codemirror.lib.CodeMirror;
@@ -32,8 +33,22 @@
     Resources.I.style().ensureInjected();
   }
 
+  interface Style extends CssResource {
+    String commentWidgets();
+    String commentBox();
+    String contents();
+    String message();
+    String header();
+    String summary();
+    String date();
+
+    String goPrev();
+    String goNext();
+    String goUp();
+  }
+
   private final CommentGroup group;
-  private OverviewBar.MarkHandle mark;
+  private ScrollbarAnnotation annotation;
   private FromTo fromTo;
   private TextMarker rangeMarker;
   private TextMarker rangeHighlightMarker;
@@ -43,8 +58,8 @@
     if (range != null) {
       fromTo = FromTo.create(range);
       rangeMarker = group.getCm().markText(
-          fromTo.getFrom(),
-          fromTo.getTo(),
+          fromTo.from(),
+          fromTo.to(),
           Configuration.create()
               .set("className", DiffTable.style.range()));
     }
@@ -79,20 +94,20 @@
     return group.getCommentManager();
   }
 
-  OverviewBar.MarkHandle getMark() {
-    return mark;
+  ScrollbarAnnotation getAnnotation() {
+    return annotation;
   }
 
-  void setMark(OverviewBar.MarkHandle mh) {
-    mark = mh;
+  void setAnnotation(ScrollbarAnnotation mh) {
+    annotation = mh;
   }
 
   void setRangeHighlight(boolean highlight) {
     if (fromTo != null) {
       if (highlight && rangeHighlightMarker == null) {
         rangeHighlightMarker = group.getCm().markText(
-            fromTo.getFrom(),
-            fromTo.getTo(),
+            fromTo.from(),
+            fromTo.to(),
             Configuration.create()
                 .set("className", DiffTable.style.rangeHighlight()));
       } else if (!highlight && rangeHighlightMarker != null) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentGroup.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentGroup.java
index 7bcf4da..a8a56fc 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentGroup.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentGroup.java
@@ -218,8 +218,8 @@
   private void updateSelection() {
     if (cm.somethingSelected()) {
       FromTo r = cm.getSelectedRange();
-      if (r.getTo().getLine() >= line) {
-        cm.setSelection(r.getFrom(), r.getTo());
+      if (r.to().line() >= line) {
+        cm.setSelection(r.from(), r.to());
       }
     }
   }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java
index 77c93a7..8d8f117 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java
@@ -26,7 +26,7 @@
 
 import net.codemirror.lib.CodeMirror;
 import net.codemirror.lib.CodeMirror.LineHandle;
-import net.codemirror.lib.LineCharacter;
+import net.codemirror.lib.Pos;
 import net.codemirror.lib.TextMarker.FromTo;
 
 import java.util.ArrayList;
@@ -92,7 +92,7 @@
         // on either side of the editor pair.
         SortedMap<Integer, CommentGroup> map = map(src.side());
         int line = src.hasActiveLine()
-            ? src.getLineNumber(src.getActiveLine()) + 1
+            ? src.getLineNumber(src.activeLine()) + 1
             : 0;
         if (dir == Direction.NEXT) {
           map = map.tailMap(line + 1);
@@ -115,8 +115,8 @@
 
         CodeMirror cm = g.getCm();
         double y = cm.heightAtLine(g.getLine() - 1, "local");
-        cm.setCursor(LineCharacter.create(g.getLine() - 1));
-        cm.scrollToY(y - 0.5 * cm.getScrollbarV().getClientHeight());
+        cm.setCursor(Pos.create(g.getLine() - 1));
+        cm.scrollToY(y - 0.5 * cm.scrollbarV().getClientHeight());
         cm.focus();
       }
     };
@@ -159,10 +159,9 @@
             getPatchSetIdFromSide(side),
             info);
         group.add(box);
-        box.setMark(host.diffTable.overview.add(
+        box.setAnnotation(host.diffTable.scrollbar.comment(
             host.getCmFromSide(side),
-            Math.max(0, info.line() - 1), 1,
-            OverviewBar.MarkType.COMMENT));
+            Math.max(0, info.line() - 1)));
         published.put(info.id(), box);
       }
     }
@@ -223,10 +222,9 @@
     }
 
     group.add(box);
-    box.setMark(host.diffTable.overview.add(
+    box.setAnnotation(host.diffTable.scrollbar.draft(
         host.getCmFromSide(side),
-        Math.max(0, info.line() - 1), 1,
-        OverviewBar.MarkType.DRAFT));
+        Math.max(0, info.line() - 1)));
     return box;
   }
 
@@ -299,7 +297,7 @@
       public void run() {
         if (cm.hasActiveLine()) {
           CommentGroup w = map(cm.side()).get(
-              cm.getLineNumber(cm.getActiveLine()) + 1);
+              cm.getLineNumber(cm.activeLine()) + 1);
           if (w != null) {
             w.openCloseLast();
           }
@@ -314,7 +312,7 @@
       public void run() {
         if (cm.hasActiveLine()) {
           CommentGroup w = map(cm.side()).get(
-              cm.getLineNumber(cm.getActiveLine()) + 1);
+              cm.getLineNumber(cm.activeLine()) + 1);
           if (w != null) {
             w.openCloseAll();
           }
@@ -330,7 +328,7 @@
         public void run() {
           String token = host.getToken();
           if (cm.hasActiveLine()) {
-            LineHandle handle = cm.getActiveLine();
+            LineHandle handle = cm.activeLine();
             int line = cm.getLineNumber(handle) + 1;
             token += "@" + (cm.side() == DisplaySide.A ? "a" : "") + line;
           }
@@ -350,13 +348,13 @@
   }
 
   private void newDraft(CodeMirror cm) {
-    int line = cm.getLineNumber(cm.getActiveLine()) + 1;
+    int line = cm.getLineNumber(cm.activeLine()) + 1;
     if (cm.somethingSelected()) {
       FromTo fromTo = cm.getSelectedRange();
-      LineCharacter end = fromTo.getTo();
-      if (end.getCh() == 0) {
-        end.setLine(end.getLine() - 1);
-        end.setCh(cm.getLine(end.getLine()).length());
+      Pos end = fromTo.to();
+      if (end.ch() == 0) {
+        end.line(end.line() - 1);
+        end.ch(cm.getLine(end.line()).length());
       }
 
       addDraftBox(cm.side(), CommentInfo.create(
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentRange.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentRange.java
index 9887dbf..a38a6ca 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentRange.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentRange.java
@@ -16,7 +16,7 @@
 
 import com.google.gwt.core.client.JavaScriptObject;
 
-import net.codemirror.lib.LineCharacter;
+import net.codemirror.lib.Pos;
 import net.codemirror.lib.TextMarker.FromTo;
 
 public class CommentRange extends JavaScriptObject {
@@ -31,11 +31,11 @@
       return null;
     }
 
-    LineCharacter from = fromTo.getFrom();
-    LineCharacter to = fromTo.getTo();
+    Pos from = fromTo.from();
+    Pos to = fromTo.to();
     return create(
-        from.getLine() + 1, from.getCh(),
-        to.getLine() + 1, to.getCh());
+        from.line() + 1, from.ch(),
+        to.line() + 1, to.ch());
   }
 
   public final native int start_line() /*-{ return this.start_line; }-*/;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java
index fa47954..8bdc747 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java
@@ -61,7 +61,7 @@
 
   @UiField Element cmA;
   @UiField Element cmB;
-  @UiField OverviewBar overview;
+  Scrollbar scrollbar;
   @UiField Element patchSetNavRow;
   @UiField Element patchSetNavCellA;
   @UiField Element patchSetNavCellB;
@@ -92,6 +92,7 @@
     PatchSetSelectBox2.link(patchSetSelectBoxA, patchSetSelectBoxB);
 
     initWidget(uiBinder.createAndBindUi(this));
+    this.scrollbar = new Scrollbar(this);
     this.parent = parent;
     this.headerVisible = true;
     this.visibleA = true;
@@ -211,7 +212,6 @@
   }
 
   void refresh() {
-    overview.refresh();
     if (header) {
       CodeMirror cm = parent.getCmFromSide(DisplaySide.A);
       diffHeaderText.getStyle().setMarginLeft(
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml
index e08a25d..1420d3b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml
@@ -19,9 +19,10 @@
     xmlns:d='urn:import:com.google.gerrit.client.diff'>
   <ui:style type='com.google.gerrit.client.diff.DiffTable.DiffTableStyle'>
     @external .CodeMirror, .CodeMirror-lines, .CodeMirror-selectedtext;
-    @external .CodeMirror-linenumber, .CodeMirror-vscrollbar .CodeMirror-scroll;
+    @external .CodeMirror-linenumber;
+    @external .CodeMirror-overlayscroll-vertical, .CodeMirror-scroll;
     @external .CodeMirror-dialog-bottom;
-    @external .cm-keymap-fat-cursor, CodeMirror-cursor;
+    @external .cm-animate-fat-cursor, .CodeMirror-cursor;
     @external .cm-searching, .cm-trailingspace, .cm-tab;
 
     .fullscreen {
@@ -75,14 +76,9 @@
     .hideA .psNavB, .hideA .b { width: 100% }
     .hideB .psNavA, .hideB .a { width: 100% }
 
-    .overview {
-      width: 10px;
-      vertical-align: top;
-    }
-
-    /* Hide scrollbars, OverviewBar controls both views. */
-    .difftable .CodeMirror-scroll { padding-right: 0; }
-    .difftable .CodeMirror-vscrollbar { display: none !important; }
+    /* Hide scrollbars on A, B controls both views. */
+    .a .CodeMirror-scroll { margin-right: -36px; }
+    .a .CodeMirror-overlayscroll-vertical { display: none !important; }
 
     .showLineNumbers .b { border-left: none; }
     .b { border-left: 1px solid #ddd; }
@@ -134,11 +130,20 @@
       height: 1.11em;
       cursor: pointer;
     }
-    .difftable .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor {
+    .difftable .CodeMirror div.CodeMirror-cursor {
       background: transparent;
       text-decoration: underline;
+      border: none;
       z-index: 2;
     }
+    .difftable .cm-animate-fat-cursor {
+      text-decoration: underline;
+      border: none;
+      animation: none;
+      -webkit-animation: none;
+      -moz-animation: none;
+      -o-animation: none;
+    }
     .difftable .CodeMirror-dialog-bottom {
       border-top: 0;
       border-left: 1px solid #000;
@@ -186,16 +191,13 @@
         <td ui:field='patchSetNavCellB' class='{style.psNavB}'>
           <d:PatchSetSelectBox2 ui:field='patchSetSelectBoxB' />
         </td>
-        <td class='{style.overview}' />
       </tr>
       <tr ui:field='diffHeaderRow' class='{style.diff_header}'>
         <td colspan='2'><pre ui:field='diffHeaderText' /></td>
-        <td class='{style.overview}' />
       </tr>
       <tr>
         <td ui:field='cmA' class='{style.a}' />
         <td ui:field='cmB' class='{style.b}' />
-        <td class='{style.overview}'><d:OverviewBar ui:field='overview'/></td>
       </tr>
     </table>
     <g:FlowPanel ui:field='widgets' visible='false'/>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.java
index 2793d01..a3ccb6f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.java
@@ -236,14 +236,14 @@
     getCommentManager().setUnsaved(this, false);
     setRangeHighlight(false);
     clearRange();
-    getMark().remove();
+    getAnnotation().remove();
     getCommentGroup().remove(this);
     getCm().focus();
   }
 
   private void restoreSelection() {
     if (getFromTo() != null && comment.in_reply_to() == null) {
-      getCm().setSelection(getFromTo().getFrom(), getFromTo().getTo());
+      getCm().setSelection(getFromTo().from(), getFromTo().to());
     }
   }
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/EditIterator.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/EditIterator.java
index 4c8d80e..ce3a562 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/EditIterator.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/EditIterator.java
@@ -16,7 +16,7 @@
 
 import com.google.gwt.core.client.JsArrayString;
 
-import net.codemirror.lib.LineCharacter;
+import net.codemirror.lib.Pos;
 
 /** An iterator for intraline edits */
 class EditIterator {
@@ -30,13 +30,13 @@
     startLine = start;
   }
 
-  LineCharacter advance(int numOfChar) {
+  Pos advance(int numOfChar) {
     numOfChar = adjustForNegativeDelta(numOfChar);
 
     while (line < lines.length()) {
       int len = lines.get(line).length() - pos + 1; // + 1 for LF
       if (numOfChar < len) {
-        LineCharacter at = LineCharacter.create(
+        Pos at = Pos.create(
             startLine + line,
             numOfChar + pos);
         pos += numOfChar;
@@ -48,7 +48,7 @@
       pos = 0;
 
       if (numOfChar == 0) {
-        return LineCharacter.create(startLine + line, 0);
+        return Pos.create(startLine + line, 0);
       }
     }
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/OverviewBar.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/OverviewBar.java
deleted file mode 100644
index b9e6375..0000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/OverviewBar.java
+++ /dev/null
@@ -1,266 +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.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Element;
-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.event.dom.client.MouseDownEvent;
-import com.google.gwt.event.dom.client.MouseMoveEvent;
-import com.google.gwt.event.dom.client.MouseUpEvent;
-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.DOM;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.Widget;
-
-import net.codemirror.lib.CodeMirror;
-import net.codemirror.lib.LineCharacter;
-import net.codemirror.lib.ScrollInfo;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/** Displays overview of all edits and comments in this file. */
-class OverviewBar extends Composite implements ClickHandler {
-  interface Binder extends UiBinder<HTMLPanel, OverviewBar> {}
-  private static final Binder uiBinder = GWT.create(Binder.class);
-
-  interface Style extends CssResource {
-    String gutter();
-    String halfGutter();
-    String comment();
-    String draft();
-    String insert();
-    String delete();
-    String viewportDrag();
-  }
-
-  enum MarkType {
-    COMMENT, DRAFT, INSERT, DELETE, EDIT
-  }
-
-  @UiField Style style;
-  @UiField Label viewport;
-
-  private final List<MarkHandle> diff;
-  private final Set<MarkHandle> comments;
-  private CodeMirror cmB;
-
-  private boolean dragging;
-  private int startY;
-  private double ratio;
-
-  OverviewBar() {
-    initWidget(uiBinder.createAndBindUi(this));
-    diff = new ArrayList<>();
-    comments = new HashSet<>();
-    addDomHandler(this, ClickEvent.getType());
-  }
-
-  void init(CodeMirror cmB) {
-    this.cmB = cmB;
-  }
-
-  void refresh() {
-    update(cmB.getScrollInfo());
-  }
-
-  void update(ScrollInfo si) {
-    double viewHeight = si.getClientHeight();
-    double r = ratio(si);
-
-    com.google.gwt.dom.client.Style style = viewport.getElement().getStyle();
-    style.setTop(si.getTop() * r, Unit.PX);
-    style.setHeight(Math.max(10, viewHeight * r), Unit.PX);
-    getElement().getStyle().setHeight(viewHeight, Unit.PX);
-    for (MarkHandle info : diff) {
-      info.position(r);
-    }
-    for (MarkHandle info : comments) {
-      info.position(r);
-    }
-  }
-
-  @Override
-  protected void onUnload() {
-    super.onUnload();
-    if (dragging) {
-      DOM.releaseCapture(viewport.getElement());
-    }
-  }
-
-  @Override
-  public void onClick(ClickEvent e) {
-    if (e.getY() < viewport.getElement().getOffsetTop()) {
-      CodeMirror.handleVimKey(cmB, "<PageUp>");
-    } else {
-      CodeMirror.handleVimKey(cmB, "<PageDown>");
-    }
-    cmB.focus();
-  }
-
-  @UiHandler("viewport")
-  void onMouseDown(MouseDownEvent e) {
-    if (cmB != null) {
-      dragging = true;
-      ratio = ratio(cmB.getScrollInfo());
-      startY = e.getY();
-      viewport.addStyleName(style.viewportDrag());
-      DOM.setCapture(viewport.getElement());
-      e.preventDefault();
-      e.stopPropagation();
-    }
-  }
-
-  @UiHandler("viewport")
-  void onMouseMove(MouseMoveEvent e) {
-    if (dragging) {
-      int y = e.getRelativeY(getElement()) - startY;
-      cmB.scrollToY(Math.max(0, y / ratio));
-      e.preventDefault();
-      e.stopPropagation();
-    }
-  }
-
-  @UiHandler("viewport")
-  void onMouseUp(MouseUpEvent e) {
-    if (dragging) {
-      dragging = false;
-      DOM.releaseCapture(viewport.getElement());
-      viewport.removeStyleName(style.viewportDrag());
-      e.preventDefault();
-      e.stopPropagation();
-    }
-  }
-
-  private double ratio(ScrollInfo si) {
-    double barHeight = si.getClientHeight();
-    double contentHeight = si.getHeight();
-    return barHeight / contentHeight;
-  }
-
-  MarkHandle add(CodeMirror cm, int line, int height, MarkType type) {
-    MarkHandle mark = new MarkHandle(cm, line, height);
-    switch (type) {
-      case COMMENT:
-        mark.addStyleName(style.comment());
-        comments.add(mark);
-        break;
-      case DRAFT:
-        mark.addStyleName(style.draft());
-        mark.getElement().setInnerText("*");
-        comments.add(mark);
-        break;
-      case INSERT:
-        mark.addStyleName(style.insert());
-        diff.add(mark);
-        break;
-      case DELETE:
-        mark.addStyleName(style.delete());
-        diff.add(mark);
-        break;
-      case EDIT:
-        mark.edit = DOM.createDiv();
-        mark.edit.setClassName(style.halfGutter());
-        mark.getElement().appendChild(mark.edit);
-        mark.addStyleName(style.insert());
-        diff.add(mark);
-        break;
-    }
-    if (cmB != null) {
-      mark.position(ratio(cmB.getScrollInfo()));
-    }
-    ((HTMLPanel) getWidget()).add(mark);
-    return mark;
-  }
-
-  void clearDiffMarkers() {
-    for (MarkHandle mark : diff) {
-      mark.removeFromParent();
-    }
-    diff.clear();
-  }
-
-  class MarkHandle extends Widget implements ClickHandler {
-    private static final int MIN_HEIGHT = 3;
-
-    private final CodeMirror cm;
-    private final int line;
-    private final int height;
-    private Element edit;
-
-    MarkHandle(CodeMirror cm, int line, int height) {
-      this.cm = cm;
-      this.line = line;
-      this.height = height;
-
-      setElement((Element)(DOM.createDiv()));
-      setStyleName(style.gutter());
-      addDomHandler(this, ClickEvent.getType());
-    }
-
-    void position(double ratio) {
-      double y = cm.heightAtLine(line, "local");
-      getElement().getStyle().setTop(y * ratio, Unit.PX);
-      if (height > 1) {
-        double e = cm.heightAtLine(line + height, "local");
-        double h = Math.max(MIN_HEIGHT, (e - y) * ratio);
-        getElement().getStyle().setHeight(h, Unit.PX);
-        if (edit != null) {
-          edit.getStyle().setHeight(h, Unit.PX);
-        }
-      }
-    }
-
-    @Override
-    public void onClick(ClickEvent e) {
-      if (height == 1 || !visible()) {
-        e.stopPropagation();
-
-        double y = cm.heightAtLine(line, "local");
-        double viewport = cm.getScrollInfo().getClientHeight();
-        cm.setCursor(LineCharacter.create(line));
-        cm.scrollToY(y - 0.5 * viewport);
-        cm.focus();
-      }
-    }
-
-    private boolean visible() {
-      int markT = getElement().getOffsetTop();
-      int markE = markT + getElement().getOffsetHeight();
-
-      int viewT = viewport.getElement().getOffsetTop();
-      int viewE = viewT + viewport.getElement().getOffsetHeight();
-
-      return (viewT <= markT && markT < viewE) // mark top within viewport
-          || (viewT <= markE && markE < viewE) // mark end within viewport
-          || (markT <= viewT && viewE <= markE); // mark contains viewport
-    }
-
-    void remove() {
-      removeFromParent();
-      comments.remove(this);
-    }
-  }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/OverviewBar.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/OverviewBar.ui.xml
deleted file mode 100644
index a56c3da..0000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/OverviewBar.ui.xml
+++ /dev/null
@@ -1,71 +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 type='com.google.gerrit.client.diff.OverviewBar.Style'>
-    .overview {
-      position: relative;
-    }
-    .gutter {
-      cursor: pointer;
-      position: absolute;
-      height: 3px;
-      width: 6px;
-      border: 1px solid grey;
-      margin-left: 1px;
-    }
-    .halfGutter {
-      cursor: pointer;
-      position: absolute;
-      height: 3px;
-      width: 3px;
-      background-color: #faa;
-    }
-    .comment, .draft {
-      background-color: #fcfa96;
-      z-index: 2;
-    }
-    .draft {
-      text-align: center;
-      font-size: small;
-      line-height: 0.5;
-      color: inherit !important;
-      text-decoration: none !important;
-    }
-    .delete {
-      background-color: #faa;
-    }
-    .insert {
-      background-color: #9f9;
-    }
-    .viewport {
-      position: absolute;
-      background-color: rgba(128, 128, 128, 0.50);
-      border: 1px solid #444;
-      width: 8px;
-      cursor: default;
-      z-index: 3;
-      border-radius: 4px;
-    }
-    .viewportDrag {
-      cursor: move;
-    }
-  </ui:style>
-  <g:HTMLPanel styleName='{style.overview}'>
-    <g:Label ui:field='viewport' styleName='{style.viewport}'/>
-  </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.java
index f09e823..c5ce0b5c 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.java
@@ -33,8 +33,6 @@
 import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
 import com.google.gerrit.reviewdb.client.Patch.ChangeType;
 import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.core.client.Scheduler.RepeatingCommand;
 import com.google.gwt.event.dom.client.ChangeEvent;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.KeyDownEvent;
@@ -348,25 +346,25 @@
 
   @UiHandler("mode")
   void onMode(@SuppressWarnings("unused") ChangeEvent e) {
-    final String m = mode.getValue(mode.getSelectedIndex());
+    String m = mode.getValue(mode.getSelectedIndex());
+    final String mode = m != null && !m.isEmpty() ? m : null;
+
     prefs.syntaxHighlighting(true);
     syntaxHighlighting.setValue(true, false);
-    Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
+    new ModeInjector().add(mode).inject(new GerritCallback<Void>() {
       @Override
-      public boolean execute() {
+      public void onSuccess(Void result) {
         if (prefs.syntaxHighlighting() && view.isAttached()) {
           view.operation(new Runnable() {
             @Override
             public void run() {
-              String mode = m != null && !m.isEmpty() ? m : null;
               view.getCmFromSide(DisplaySide.A).setOption("mode", mode);
               view.getCmFromSide(DisplaySide.B).setOption("mode", mode);
             }
           });
         }
-        return false;
       }
-    }, 50);
+    });
   }
 
   @UiHandler("whitespaceErrors")
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Resources.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Resources.java
index 605935b..e379d60 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Resources.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Resources.java
@@ -16,29 +16,16 @@
 
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.resources.client.ClientBundle;
-import com.google.gwt.resources.client.CssResource;
 import com.google.gwt.resources.client.ImageResource;
 
 /** Resources used by diff. */
 interface Resources extends ClientBundle {
   static final Resources I = GWT.create(Resources.class);
 
-  @Source("CommentBoxUi.css") Style style();
+  @Source("CommentBox.css") CommentBox.Style style();
+  @Source("Scrollbar.css") Scrollbar.Style scrollbarStyle();
+
   @Source("goPrev.png") ImageResource goPrev();
   @Source("goNext.png") ImageResource goNext();
   @Source("goUp.png") ImageResource goUp();
-
-  interface Style extends CssResource {
-    String commentWidgets();
-    String commentBox();
-    String contents();
-    String message();
-    String header();
-    String summary();
-    String date();
-
-    String goPrev();
-    String goNext();
-    String goUp();
-  }
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ScrollSynchronizer.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ScrollSynchronizer.java
index abff8ef..61e00c3 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ScrollSynchronizer.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ScrollSynchronizer.java
@@ -22,7 +22,6 @@
 class ScrollSynchronizer {
   private DiffTable diffTable;
   private LineMapper mapper;
-  private OverviewBar overview;
   private ScrollCallback active;
   private ScrollCallback callbackA;
   private ScrollCallback callbackB;
@@ -32,7 +31,6 @@
       LineMapper mapper) {
     this.diffTable = diffTable;
     this.mapper = mapper;
-    this.overview = diffTable.overview;
 
     callbackA = new ScrollCallback(cmA, cmB, DisplaySide.A);
     callbackB = new ScrollCallback(cmB, cmA, DisplaySide.B);
@@ -45,9 +43,9 @@
   }
 
   private void updateScreenHeader(ScrollInfo si) {
-    if (si.getTop() == 0 && !diffTable.isHeaderVisible()) {
+    if (si.top() == 0 && !diffTable.isHeaderVisible()) {
       diffTable.setHeaderVisible(true);
-    } else if (si.getTop() > 0.5 * si.getClientHeight()
+    } else if (si.top() > 0.5 * si.clientHeight()
         && diffTable.isHeaderVisible()) {
       diffTable.setHeaderVisible(false);
     }
@@ -75,7 +73,7 @@
     }
 
     void sync() {
-      dst.scrollToY(align(src.getScrollInfo().getTop()));
+      dst.scrollToY(align(src.getScrollInfo().top()));
     }
 
     @Override
@@ -87,8 +85,7 @@
       if (active == this) {
         ScrollInfo si = src.getScrollInfo();
         updateScreenHeader(si);
-        overview.update(si);
-        dst.scrollTo(si.getLeft(), align(si.getTop()));
+        dst.scrollTo(si.left(), align(si.top()));
         state = 0;
       }
     }
@@ -97,7 +94,7 @@
       switch (state) {
         case 0:
           state = 1;
-          dst.scrollToY(align(src.getScrollInfo().getTop()));
+          dst.scrollToY(align(src.getScrollInfo().top()));
           break;
         case 1:
           state = 2;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Scrollbar.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Scrollbar.css
new file mode 100644
index 0000000..a535115
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Scrollbar.css
@@ -0,0 +1,58 @@
+/* Copyright (C) 2014 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.
+ */
+
+@external .CodeMirror;
+@external .CodeMirror-overlayscroll-horizontal;
+@external .CodeMirror-overlayscroll-vertical;
+
+.CodeMirror-overlayscroll-horizontal div {
+  min-width: 25px;
+}
+.CodeMirror-overlayscroll-vertical div {
+  min-height: 25px;
+}
+
+.CodeMirror .CodeMirror-overlayscroll-vertical {
+  z-index: inherit;
+}
+.CodeMirror .CodeMirror-overlayscroll-horizontal div,
+.CodeMirror .CodeMirror-overlayscroll-vertical div {
+  background-color: rgba(128, 128, 128, 0.50);
+  z-index: 8;
+}
+
+.comment, .draft, .insert, .delete, .edit {
+  min-height: 5px;
+  position: absolute;
+  right: 0;
+  z-index: 7;
+}
+
+.comment, .draft {
+  color: #0d0d0d;
+  font-size: 9px;
+}
+
+.delete {
+  background-color: #faa;
+}
+.insert {
+  background-color: #9f9;
+}
+.edit {
+  border-left: 3px solid #faa;
+  width: 2px !important;
+  background-color: #9f9;
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Scrollbar.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Scrollbar.java
new file mode 100644
index 0000000..b72ab43
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Scrollbar.java
@@ -0,0 +1,99 @@
+// Copyright (C) 2014 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.gwt.resources.client.CssResource;
+
+import net.codemirror.lib.CodeMirror;
+import net.codemirror.lib.Pos;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Displays overview of all edits and comments in this file. */
+class Scrollbar {
+  static {
+    Resources.I.scrollbarStyle().ensureInjected();
+  }
+
+  interface Style extends CssResource {
+    String comment();
+    String draft();
+    String insert();
+    String delete();
+    String edit();
+  }
+
+  private final List<ScrollbarAnnotation> diff = new ArrayList<>();
+  private final DiffTable parent;
+
+  Scrollbar(DiffTable d) {
+    parent = d;
+  }
+
+  ScrollbarAnnotation comment(CodeMirror cm, int line) {
+    ScrollbarAnnotation a = new ScrollbarAnnotation(cm);
+    a.setStyleName(Resources.I.scrollbarStyle().comment());
+    a.at(line);
+    a.getElement().setInnerText("\u2736"); // Six pointed black star
+    parent.add(a);
+    return a;
+  }
+
+  ScrollbarAnnotation draft(CodeMirror cm, int line) {
+    ScrollbarAnnotation a = new ScrollbarAnnotation(cm);
+    a.setStyleName(Resources.I.scrollbarStyle().draft());
+    a.at(line);
+    a.getElement().setInnerText("\u270D"); // Writing hand
+    parent.add(a);
+    return a;
+  }
+
+  ScrollbarAnnotation insert(CodeMirror cm, int line, int len) {
+    ScrollbarAnnotation a = diff(cm, line, len);
+    a.setStyleName(Resources.I.scrollbarStyle().insert());
+    parent.add(a);
+    return a;
+  }
+
+  ScrollbarAnnotation delete(CodeMirror cmA, CodeMirror cmB, int line, int len) {
+    ScrollbarAnnotation a = diff(cmA, line, len);
+    a.setStyleName(Resources.I.scrollbarStyle().delete());
+    a.renderOn(cmB);
+    parent.add(a);
+    return a;
+  }
+
+  ScrollbarAnnotation edit(CodeMirror cm, int line, int len) {
+    ScrollbarAnnotation a = diff(cm, line, len);
+    a.setStyleName(Resources.I.scrollbarStyle().edit());
+    parent.add(a);
+    return a;
+  }
+
+  private ScrollbarAnnotation diff(CodeMirror cm, int s, int n) {
+    ScrollbarAnnotation a = new ScrollbarAnnotation(cm);
+    a.at(Pos.create(s), Pos.create(s + n));
+    diff.add(a);
+    return a;
+  }
+
+  void removeDiffAnnotations() {
+    for (ScrollbarAnnotation a : diff) {
+      a.remove();
+    }
+    diff.clear();
+  }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ScrollbarAnnotation.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ScrollbarAnnotation.java
new file mode 100644
index 0000000..c8f9911
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ScrollbarAnnotation.java
@@ -0,0 +1,119 @@
+// Copyright (C) 2014 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.gwt.dom.client.Element;
+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.user.client.DOM;
+import com.google.gwt.user.client.ui.Widget;
+
+import net.codemirror.lib.CodeMirror;
+import net.codemirror.lib.CodeMirror.RegisteredHandler;
+import net.codemirror.lib.Pos;
+
+/** Displayed on the vertical scrollbar to place a chunk or comment. */
+class ScrollbarAnnotation extends Widget implements ClickHandler {
+  private final CodeMirror cm;
+  private CodeMirror cmB;
+  private RegisteredHandler refresh;
+  private Pos from;
+  private Pos to;
+  private double scale;
+
+  ScrollbarAnnotation(CodeMirror cm) {
+    setElement((Element) DOM.createDiv());
+    getElement().setAttribute("not-content", "true");
+    addDomHandler(this, ClickEvent.getType());
+    this.cm = cm;
+    this.cmB = cm;
+  }
+
+  void remove() {
+    removeFromParent();
+  }
+
+  void at(int line) {
+    at(Pos.create(line), Pos.create(line + 1));
+  }
+
+  void at(Pos from, Pos to) {
+    this.from = from;
+    this.to = to;
+  }
+
+  void renderOn(CodeMirror cm) {
+    this.cmB = cm;
+  }
+
+  @Override
+  protected void onLoad() {
+    cmB.getWrapperElement().appendChild(getElement());
+    refresh = cmB.on("refresh", new Runnable() {
+      @Override
+      public void run() {
+        if (updateScale()) {
+          updatePosition();
+        }
+      }
+    });
+    updateScale();
+    updatePosition();
+  }
+
+  @Override
+  protected void onUnload() {
+    cmB.off("refresh", refresh);
+  }
+
+  private boolean updateScale() {
+    double old = scale;
+    double docHeight = cmB.getWrapperElement().getClientHeight();
+    double lineHeight = cmB.heightAtLine(cmB.lastLine() + 1, "local");
+    scale = (docHeight - cmB.barHeight()) / lineHeight;
+    return old != scale;
+  }
+
+  private void updatePosition() {
+    double top = cm.charCoords(from, "local").top() * scale;
+    double bottom = cm.charCoords(to, "local").bottom() * scale;
+
+    Element e = getElement();
+    e.getStyle().setTop(top, Unit.PX);
+    e.getStyle().setWidth(Math.max(2, cm.barWidth() - 1), Unit.PX);
+    e.getStyle().setHeight(Math.max(3, bottom - top), Unit.PX);
+  }
+
+  @Override
+  public void onClick(ClickEvent event) {
+    event.stopPropagation();
+
+    int line = from.line();
+    int h = to.line() - line;
+    if (h > 5) {
+      // Map click inside of the annotation to the relative position
+      // within the region covered by the annotation.
+      double s = ((double) event.getY()) / getElement().getOffsetHeight();
+      line += (int) (s * h);
+    }
+
+    double y = cm.heightAtLine(line, "local");
+    double viewport = cm.getScrollInfo().clientHeight();
+    cm.setCursor(from);
+    cm.scrollTo(0, y - 0.5 * viewport);
+    cm.focus();
+  }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide2.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide2.java
index 11c8745..4b44612 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide2.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide2.java
@@ -75,8 +75,8 @@
 import net.codemirror.lib.CodeMirror.LineHandle;
 import net.codemirror.lib.Configuration;
 import net.codemirror.lib.KeyMap;
-import net.codemirror.lib.LineCharacter;
 import net.codemirror.lib.ModeInjector;
+import net.codemirror.lib.Pos;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -294,10 +294,10 @@
       if (cm.lineAtHeight(height - 20) < line) {
         cm.scrollToY(cm.heightAtLine(line, "local") - 0.5 * height);
       }
-      cm.setCursor(LineCharacter.create(line));
+      cm.setCursor(Pos.create(line));
       cm.focus();
     } else {
-      cmA.setCursor(LineCharacter.create(0));
+      cmA.setCursor(Pos.create(0));
       cmA.focus();
     }
     if (Gerrit.isSignedIn() && prefs.autoReview()) {
@@ -393,19 +393,19 @@
         .on("Space", new Runnable() {
           @Override
           public void run() {
-            CodeMirror.handleVimKey(cm, "<C-d>");
+            cm.vim().handleKey("<C-d>");
           }
         })
         .on("Shift-Space", new Runnable() {
           @Override
           public void run() {
-            CodeMirror.handleVimKey(cm, "<C-u>");
+            cm.vim().handleKey("<C-u>");
           }
         })
         .on("Ctrl-F", new Runnable() {
           @Override
           public void run() {
-            CodeMirror.handleVimKey(cm, "/");
+            cm.vim().handleKey("/");
           }
         })
         .on("Ctrl-A", new Runnable() {
@@ -424,10 +424,8 @@
       private InsertCommentBubble bubble;
 
       @Override
-      public void handle(CodeMirror cm, LineCharacter anchor, LineCharacter head) {
-        if (anchor == head
-            || (anchor.getLine() == head.getLine()
-             && anchor.getCh() == head.getCh())) {
+      public void handle(CodeMirror cm, Pos anchor, Pos head) {
+        if (anchor.equals(head)) {
           if (bubble != null) {
             bubble.setVisible(false);
           }
@@ -440,10 +438,10 @@
         bubble.position(cm.charCoords(head, "local"));
       }
 
-      private void init(LineCharacter anchor) {
+      private void init(Pos anchor) {
         bubble = new InsertCommentBubble(commentManager, cm);
         add(bubble);
-        cm.addWidget(anchor, bubble.getElement(), false);
+        cm.addWidget(anchor, bubble.getElement());
       }
     };
   }
@@ -551,18 +549,17 @@
       diffTable.addStyleName(DiffTable.style.showLineNumbers());
     }
 
-    cmA = newCM(diff.meta_a(), diff.text_a(), DisplaySide.A, diffTable.cmA);
-    cmB = newCM(diff.meta_b(), diff.text_b(), DisplaySide.B, diffTable.cmB);
-    diffTable.overview.init(cmB);
-    chunkManager = new ChunkManager(this, cmA, cmB, diffTable.overview);
+    cmA = newCM(diff.meta_a(), diff.text_a(), diffTable.cmA).side(DisplaySide.A);
+    cmB = newCM(diff.meta_b(), diff.text_b(), diffTable.cmB).side(DisplaySide.B);
+    chunkManager = new ChunkManager(this, cmA, cmB, diffTable.scrollbar);
     skipManager = new SkipManager(this, commentManager);
 
     columnMarginA = DOM.createDiv();
     columnMarginB = DOM.createDiv();
     columnMarginA.setClassName(DiffTable.style.columnMargin());
     columnMarginB.setClassName(DiffTable.style.columnMargin());
-    cmA.getMoverElement().appendChild(columnMarginA);
-    cmB.getMoverElement().appendChild(columnMarginB);
+    cmA.mover().appendChild(columnMarginA);
+    cmB.mover().appendChild(columnMarginB);
 
     if (prefs.renderEntireFile() && !canEnableRenderEntireFile(prefs)) {
       // CodeMirror is too slow to layout an entire huge file.
@@ -637,19 +634,16 @@
   private CodeMirror newCM(
       DiffInfo.FileMeta meta,
       String contents,
-      DisplaySide side,
       Element parent) {
-    String mode = fileSize == FileSize.SMALL
-        ? getContentType(meta)
-        : null;
-    return CodeMirror.create(side, parent, Configuration.create()
+    return CodeMirror.create(parent, Configuration.create()
       .set("readOnly", true)
       .set("cursorBlinkRate", 0)
       .set("cursorHeight", 0.85)
       .set("lineNumbers", prefs.showLineNumbers())
       .set("tabSize", prefs.tabSize())
-      .set("mode", mode)
+      .set("mode", fileSize == FileSize.SMALL ? getContentType(meta) : null)
       .set("lineWrapping", false)
+      .set("scrollbarStyle", "overlay")
       .set("styleSelectedText", true)
       .set("showTrailingSpace", prefs.showWhitespaceErrors())
       .set("keyMap", "vim_ro")
@@ -706,7 +700,7 @@
         e.appendChild(pre);
       }
 
-      cmB.getMeasureElement().appendChild(p);
+      cmB.measure().appendChild(p);
       lineHeightPx = ((double) p.getOffsetHeight()) / lines;
       p.removeFromParent();
     }
@@ -724,11 +718,11 @@
       e.getStyle().setDisplay(Style.Display.INLINE_BLOCK);
       e.setInnerText(s.toString());
 
-      cmA.getMeasureElement().appendChild(e);
+      cmA.measure().appendChild(e);
       double a = ((double) e.getOffsetWidth()) / len;
       e.removeFromParent();
 
-      cmB.getMeasureElement().appendChild(e);
+      cmB.measure().appendChild(e);
       double b = ((double) e.getOffsetWidth()) / len;
       e.removeFromParent();
       charWidthPx = Math.max(a, b);
@@ -790,7 +784,6 @@
       public void run() {
         skipManager.removeAll();
         skipManager.render(context, diff);
-        diffTable.overview.refresh();
       }
     });
   }
@@ -818,10 +811,10 @@
 
   private void clearActiveLine(CodeMirror cm) {
     if (cm.hasActiveLine()) {
-      LineHandle activeLine = cm.getActiveLine();
+      LineHandle activeLine = cm.activeLine();
       cm.removeLineClass(activeLine,
           LineClassWhere.WRAP, DiffTable.style.activeLine());
-      cm.setActiveLine(null);
+      cm.activeLine(null);
     }
   }
 
@@ -841,22 +834,22 @@
             operation(new Runnable() {
               @Override
               public void run() {
-                LineHandle handle = cm.getLineHandleVisualStart(
-                    cm.getCursor("end").getLine());
-                if (cm.hasActiveLine() && cm.getActiveLine().equals(handle)) {
+                LineHandle handle =
+                    cm.getLineHandleVisualStart(cm.getCursor("end").line());
+                if (cm.hasActiveLine() && cm.activeLine().equals(handle)) {
                   return;
                 }
 
                 clearActiveLine(cm);
                 clearActiveLine(other);
-                cm.setActiveLine(handle);
+                cm.activeLine(handle);
                 cm.addLineClass(
                     handle, LineClassWhere.WRAP, DiffTable.style.activeLine());
                 LineOnOtherInfo info =
                     lineOnOther(cm.side(), cm.getLineNumber(handle));
                 if (info.isAligned()) {
                   LineHandle oLineHandle = other.getLineHandle(info.getLine());
-                  other.setActiveLine(oLineHandle);
+                  other.activeLine(oLineHandle);
                   other.addLineClass(oLineHandle, LineClassWhere.WRAP,
                       DiffTable.style.activeLine());
                 }
@@ -879,8 +872,8 @@
             && !clickEvent.getCtrlKey()
             && !clickEvent.getShiftKey()) {
           if (!(cm.hasActiveLine() &&
-              cm.getLineNumber(cm.getActiveLine()) == line)) {
-            cm.setCursor(LineCharacter.create(line));
+              cm.getLineNumber(cm.activeLine()) == line)) {
+            cm.setCursor(Pos.create(line));
           }
           Scheduler.get().scheduleDeferred(new ScheduledCommand() {
             @Override
@@ -930,9 +923,9 @@
       @Override
       public void run() {
         if (cmSrc.hasActiveLine()) {
-          cmDst.setCursor(LineCharacter.create(lineOnOther(
+          cmDst.setCursor(Pos.create(lineOnOther(
               sideSrc,
-              cmSrc.getLineNumber(cmSrc.getActiveLine())).getLine()));
+              cmSrc.getLineNumber(cmSrc.activeLine())).getLine()));
         }
         cmDst.focus();
       }
@@ -943,8 +936,8 @@
     return new Runnable() {
       @Override
       public void run() {
-        if (cm.hasVimSearchHighlight()) {
-          CodeMirror.handleVimKey(cm, "N");
+        if (cm.vim().hasSearchHighlight()) {
+          cm.vim().handleKey("N");
         } else {
           commentManager.commentNav(cm, Direction.NEXT).run();
         }
@@ -956,8 +949,8 @@
     return new Runnable() {
       @Override
       public void run() {
-        if (cm.hasVimSearchHighlight()) {
-          CodeMirror.handleVimKey(cm, "n");
+        if (cm.vim().hasSearchHighlight()) {
+          cm.vim().handleKey("n");
         } else {
           chunkManager.diffChunkNav(cm, Direction.NEXT).run();
         }
@@ -981,7 +974,6 @@
     int height = getCodeMirrorHeight();
     cmA.setHeight(height);
     cmB.setHeight(height);
-    diffTable.overview.refresh();
   }
 
   private int getCodeMirrorHeight() {
@@ -1084,7 +1076,7 @@
               public void run() {
                 skipManager.removeAll();
                 chunkManager.reset();
-                diffTable.overview.clearDiffMarkers();
+                diffTable.scrollbar.removeDiffAnnotations();
                 setShowIntraline(prefs.intralineDifference());
                 render(diff);
                 chunkManager.adjustPadding();
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 c69bc07..4c12cc0 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
@@ -30,6 +30,7 @@
 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;
 
@@ -95,8 +96,8 @@
     }
 
     textMarker = cm.markText(
-        CodeMirror.pos(start, 0),
-        CodeMirror.pos(end),
+        Pos.create(start, 0),
+        Pos.create(end),
         Configuration.create()
           .set("collapsed", true)
           .set("inclusiveLeft", true)
@@ -133,14 +134,13 @@
   void expandBefore(int cnt) {
     expandSideBefore(cnt);
     otherBar.expandSideBefore(cnt);
-    manager.getOverviewBar().refresh();
   }
 
   private void expandSideBefore(int cnt) {
     FromTo range = textMarker.find();
-    int oldStart = range.getFrom().getLine();
+    int oldStart = range.from().line();
     int newStart = oldStart + cnt;
-    int end = range.getTo().getLine();
+    int end = range.to().line();
     clearMarkerAndWidget();
     collapse(newStart, end, true);
     updateSelection();
@@ -153,8 +153,8 @@
 
   private void expandAfter() {
     FromTo range = textMarker.find();
-    int start = range.getFrom().getLine();
-    int oldEnd = range.getTo().getLine();
+    int start = range.from().line();
+    int oldEnd = range.to().line();
     int newEnd = oldEnd - NUM_ROWS_TO_EXPAND;
     boolean attach = start == 0;
     if (attach) {
@@ -169,7 +169,7 @@
   private void updateSelection() {
     if (cm.somethingSelected()) {
       FromTo sel = cm.getSelectedRange();
-      cm.setSelection(sel.getFrom(), sel.getTo());
+      cm.setSelection(sel.from(), sel.to());
     }
   }
 
@@ -185,7 +185,6 @@
     expandSideAll();
     otherBar.expandSideAll();
     manager.remove(this, otherBar);
-    manager.getOverviewBar().refresh();
   }
 
   @UiHandler("upArrow")
@@ -198,7 +197,6 @@
   void onExpandAfter(@SuppressWarnings("unused") ClickEvent e) {
     expandAfter();
     otherBar.expandAfter();
-    manager.getOverviewBar().refresh();
     cm.focus();
   }
 }
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 5ba275f..0baada5 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
@@ -38,10 +38,6 @@
     this.commentManager = commentManager;
   }
 
-  OverviewBar getOverviewBar() {
-    return host.diffTable.overview;
-  }
-
   void render(int context, DiffInfo diff) {
     if (context == AccountDiffPreference.WHOLE_FILE_CONTEXT) {
       return;
@@ -111,7 +107,6 @@
       for (SkipBar bar : skipBars) {
         bar.expandSideAll();
       }
-      getOverviewBar().refresh();
       skipBars = null;
       line0 = null;
     }
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java
index bf083b9..8255cbb 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java
+++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java
@@ -32,274 +32,267 @@
     Loader.initLibrary(cb);
   }
 
-  public static native CodeMirror create(
-      DisplaySide side,
-      Element parent,
-      Configuration cfg) /*-{
-    var m = $wnd.CodeMirror(parent, cfg);
-    m._sbs2_side = side;
-    return m;
+  public static native CodeMirror create(Element p, Configuration cfg) /*-{
+    return $wnd.CodeMirror(p, cfg);
   }-*/;
 
-  public static CodeMirror create(Element parent, Configuration cfg) {
-    return create(null, parent, cfg);
-  }
-
   public final native void setOption(String option, boolean value) /*-{
-    this.setOption(option, value);
+    this.setOption(option, value)
   }-*/;
 
   public final native void setOption(String option, double value) /*-{
-    this.setOption(option, value);
+    this.setOption(option, value)
   }-*/;
 
   public final native void setOption(String option, String value) /*-{
-    this.setOption(option, value);
+    this.setOption(option, value)
   }-*/;
 
   public final native void setOption(String option, JavaScriptObject val) /*-{
-    this.setOption(option, val);
+    this.setOption(option, val)
   }-*/;
 
   public final native String getStringOption(String o) /*-{ return this.getOption(o) }-*/;
-  public final native void setValue(String v) /*-{ this.setValue(v); }-*/;
 
-  public final native void setWidth(double w) /*-{ this.setSize(w, null); }-*/;
-  public final native void setWidth(String w) /*-{ this.setSize(w, null); }-*/;
-  public final native void setHeight(double h) /*-{ this.setSize(null, h); }-*/;
-  public final native void setHeight(String h) /*-{ this.setSize(null, h); }-*/;
+  public final native String getValue() /*-{ return this.getValue() }-*/;
+  public final native void setValue(String v) /*-{ this.setValue(v) }-*/;
+
+  public final native void setWidth(double w) /*-{ this.setSize(w, null) }-*/;
+  public final native void setWidth(String w) /*-{ this.setSize(w, null) }-*/;
+  public final native void setHeight(double h) /*-{ this.setSize(null, h) }-*/;
+  public final native void setHeight(String h) /*-{ this.setSize(null, h) }-*/;
   public final native String getLine(int n) /*-{ return this.getLine(n) }-*/;
+  public final native double barHeight() /*-{ return this.display.barHeight }-*/;
+  public final native double barWidth() /*-{ return this.display.barWidth }-*/;
+  public final native int lastLine() /*-{ return this.lastLine() }-*/;
+  public final native void refresh() /*-{ this.refresh() }-*/;
 
-  public final native void refresh() /*-{ this.refresh(); }-*/;
-  public final native Element getWrapperElement() /*-{ return this.getWrapperElement(); }-*/;
-
-  public final native TextMarker markText(LineCharacter from, LineCharacter to,
+  public final native TextMarker markText(Pos from, Pos to,
       Configuration options) /*-{
-    return this.markText(from, to, options);
+    return this.markText(from, to, options)
   }-*/;
 
   public enum LineClassWhere {
-    TEXT, BACKGROUND, WRAP
+    TEXT { @Override String value() { return "text"; } },
+    BACKGROUND { @Override String value() { return "background"; } },
+    WRAP { @Override String value() { return "wrap"; } };
+    abstract String value();
   }
 
   public final void addLineClass(int line, LineClassWhere where,
       String className) {
-    addLineClassNative(line, where.name().toLowerCase(), className);
+    addLineClassNative(line, where.value(), className);
   }
 
   private final native void addLineClassNative(int line, String where,
       String lineClass) /*-{
-    this.addLineClass(line, where, lineClass);
+    this.addLineClass(line, where, lineClass)
   }-*/;
 
   public final void addLineClass(LineHandle line, LineClassWhere where,
       String className) {
-    addLineClassNative(line, where.name().toLowerCase(), className);
+    addLineClassNative(line, where.value(), className);
   }
 
   private final native void addLineClassNative(LineHandle line, String where,
       String lineClass) /*-{
-    this.addLineClass(line, where, lineClass);
+    this.addLineClass(line, where, lineClass)
   }-*/;
 
   public final void removeLineClass(int line, LineClassWhere where,
       String className) {
-    removeLineClassNative(line, where.name().toLowerCase(), className);
+    removeLineClassNative(line, where.value(), className);
   }
 
   private final native void removeLineClassNative(int line, String where,
       String lineClass) /*-{
-    this.removeLineClass(line, where, lineClass);
+    this.removeLineClass(line, where, lineClass)
   }-*/;
 
   public final void removeLineClass(LineHandle line, LineClassWhere where,
       String className) {
-    removeLineClassNative(line, where.name().toLowerCase(), className);
+    removeLineClassNative(line, where.value(), className);
   }
 
   private final native void removeLineClassNative(LineHandle line, String where,
       String lineClass) /*-{
-    this.removeLineClass(line, where, lineClass);
+    this.removeLineClass(line, where, lineClass)
   }-*/;
 
-  public final native void addWidget(LineCharacter pos, Element node,
-      boolean scrollIntoView) /*-{
-    this.addWidget(pos, node, scrollIntoView);
+  public final native void addWidget(Pos pos, Element node) /*-{
+    this.addWidget(pos, node, false)
   }-*/;
 
   public final native LineWidget addLineWidget(int line, Element node,
       Configuration options) /*-{
-    return this.addLineWidget(line, node, options);
+    return this.addLineWidget(line, node, options)
   }-*/;
 
   public final native int lineAtHeight(double height) /*-{
-    return this.lineAtHeight(height);
+    return this.lineAtHeight(height)
   }-*/;
 
   public final native int lineAtHeight(double height, String mode) /*-{
-    return this.lineAtHeight(height, mode);
+    return this.lineAtHeight(height, mode)
   }-*/;
 
   public final native double heightAtLine(int line) /*-{
-    return this.heightAtLine(line);
+    return this.heightAtLine(line)
   }-*/;
 
   public final native double heightAtLine(int line, String mode) /*-{
-    return this.heightAtLine(line, mode);
+    return this.heightAtLine(line, mode)
   }-*/;
 
-  public final native Rect charCoords(LineCharacter pos, String mode) /*-{
-    return this.charCoords(pos, mode);
+  public final native Rect charCoords(Pos pos, String mode) /*-{
+    return this.charCoords(pos, mode)
   }-*/;
 
   public final native CodeMirrorDoc getDoc() /*-{
-    return this.getDoc();
+    return this.getDoc()
   }-*/;
 
   public final native void scrollTo(double x, double y) /*-{
-    this.scrollTo(x, y);
+    this.scrollTo(x, y)
   }-*/;
 
   public final native void scrollToY(double y) /*-{
-    this.scrollTo(null, y);
+    this.scrollTo(null, y)
   }-*/;
 
   public final native ScrollInfo getScrollInfo() /*-{
-    return this.getScrollInfo();
+    return this.getScrollInfo()
   }-*/;
 
   public final native Viewport getViewport() /*-{
-    return this.getViewport();
+    return this.getViewport()
   }-*/;
 
   public final native void operation(Runnable thunk) /*-{
     this.operation(function() {
       thunk.@java.lang.Runnable::run()();
-    });
+    })
   }-*/;
 
-  public final native void on(String event, Runnable thunk) /*-{
-    this.on(event, $entry(function() {
-      thunk.@java.lang.Runnable::run()();
-    }));
+  public final native void off(String event, RegisteredHandler h) /*-{
+    this.off(event, h)
+  }-*/;
+
+  public final native RegisteredHandler on(String event, Runnable thunk) /*-{
+    var h = $entry(function() { thunk.@java.lang.Runnable::run()() });
+    this.on(event, h);
+    return h;
   }-*/;
 
   public final native void on(String event, EventHandler handler) /*-{
     this.on(event, $entry(function(cm, e) {
       handler.@net.codemirror.lib.CodeMirror.EventHandler::handle(
-        Lnet/codemirror/lib/CodeMirror;Lcom/google/gwt/dom/client/NativeEvent;)(cm, e);
-    }));
+        Lnet/codemirror/lib/CodeMirror;
+        Lcom/google/gwt/dom/client/NativeEvent;)(cm, e);
+    }))
   }-*/;
 
   public final native void on(String event, RenderLineHandler handler) /*-{
-    this.on(event, $entry(function(cm, h, ele) {
+    this.on(event, $entry(function(cm, h, e) {
       handler.@net.codemirror.lib.CodeMirror.RenderLineHandler::handle(
-        Lnet/codemirror/lib/CodeMirror;Lnet/codemirror/lib/CodeMirror$LineHandle;
-        Lcom/google/gwt/dom/client/Element;)(cm, h, ele);
-    }));
+        Lnet/codemirror/lib/CodeMirror;
+        Lnet/codemirror/lib/CodeMirror$LineHandle;
+        Lcom/google/gwt/dom/client/Element;)(cm, h, e);
+    }))
   }-*/;
 
   public final native void on(String event, GutterClickHandler handler) /*-{
     this.on(event, $entry(function(cm, l, g, e) {
       handler.@net.codemirror.lib.CodeMirror.GutterClickHandler::handle(
-        Lnet/codemirror/lib/CodeMirror;ILjava/lang/String;
+        Lnet/codemirror/lib/CodeMirror;
+        I
+        Ljava/lang/String;
         Lcom/google/gwt/dom/client/NativeEvent;)(cm, l, g, e);
-    }));
+    }))
   }-*/;
 
   public final native void on(String event, BeforeSelectionChangeHandler handler) /*-{
-    this.on(event, $entry(function(cm, e) {
+    this.on(event, $entry(function(cm, o) {
+      var e = o.ranges[o.ranges.length-1];
       handler.@net.codemirror.lib.CodeMirror.BeforeSelectionChangeHandler::handle(
-        Lnet/codemirror/lib/CodeMirror;Lnet/codemirror/lib/LineCharacter;
-        Lnet/codemirror/lib/LineCharacter;)(cm,e.anchor,e.head);
-    }));
+        Lnet/codemirror/lib/CodeMirror;
+        Lnet/codemirror/lib/Pos;
+        Lnet/codemirror/lib/Pos;)(cm, e.anchor, e.head);
+    }))
   }-*/;
 
-  public final native LineCharacter getCursor() /*-{
-    return this.getCursor();
-  }-*/;
-
-  public final native LineCharacter getCursor(String start) /*-{
-    return this.getCursor(start);
+  public final native void setCursor(Pos p) /*-{ this.setCursor(p) }-*/;
+  public final native Pos getCursor() /*-{ return this.getCursor() }-*/;
+  public final native Pos getCursor(String start) /*-{
+    return this.getCursor(start)
   }-*/;
 
   public final FromTo getSelectedRange() {
     return FromTo.create(getCursor("start"), getCursor("end"));
   }
 
-  public final native void setSelection(LineCharacter anchor) /*-{
-    this.setSelection(anchor);
-  }-*/;
-
-  public final native void setSelection(LineCharacter anchor, LineCharacter head) /*-{
-    this.setSelection(anchor, head);
-  }-*/;
-
-  public final native void setCursor(LineCharacter lineCh) /*-{
-    this.setCursor(lineCh);
+  public final native void setSelection(Pos p) /*-{ this.setSelection(p) }-*/;
+  public final native void setSelection(Pos anchor, Pos head) /*-{
+    this.setSelection(anchor, head)
   }-*/;
 
   public final native boolean somethingSelected() /*-{
-    return this.somethingSelected();
+    return this.somethingSelected()
   }-*/;
 
   public final native boolean hasActiveLine() /*-{
-    return !!this.state.activeLine;
+    return !!this.state.activeLine
   }-*/;
 
-  public final native LineHandle getActiveLine() /*-{
-    return this.state.activeLine;
+  public final native LineHandle activeLine() /*-{
+    return this.state.activeLine
   }-*/;
 
-  public final native void setActiveLine(LineHandle line) /*-{
-    this.state.activeLine = line;
+  public final native void activeLine(LineHandle line) /*-{
+    this.state.activeLine = line
   }-*/;
 
-  public final native void addKeyMap(KeyMap map) /*-{ this.addKeyMap(map); }-*/;
-  public final native void removeKeyMap(KeyMap map) /*-{ this.removeKeyMap(map); }-*/;
-
-  public static final native LineCharacter pos(int line, int ch) /*-{
-    return $wnd.CodeMirror.Pos(line, ch);
-  }-*/;
-
-  public static final native LineCharacter pos(int line) /*-{
-    return $wnd.CodeMirror.Pos(line);
-  }-*/;
+  public final native void addKeyMap(KeyMap map) /*-{ this.addKeyMap(map) }-*/;
+  public final native void removeKeyMap(KeyMap map) /*-{ this.removeKeyMap(map) }-*/;
 
   public final native LineHandle getLineHandle(int line) /*-{
-    return this.getLineHandle(line);
+    return this.getLineHandle(line)
   }-*/;
 
   public final native LineHandle getLineHandleVisualStart(int line) /*-{
-    return this.getLineHandleVisualStart(line);
+    return this.getLineHandleVisualStart(line)
   }-*/;
 
   public final native int getLineNumber(LineHandle handle) /*-{
-    return this.getLineNumber(handle);
+    return this.getLineNumber(handle)
   }-*/;
 
   public final native void focus() /*-{
-    this.focus();
+    this.focus()
+  }-*/;
+
+  public final native Element getWrapperElement() /*-{
+    return this.getWrapperElement()
   }-*/;
 
   public final native Element getGutterElement() /*-{
-    return this.getGutterElement();
+    return this.getGutterElement()
   }-*/;
 
-  public final native Element getSizer() /*-{
-    return this.display.sizer;
+  public final native Element sizer() /*-{
+    return this.display.sizer
   }-*/;
 
-  public final native Element getMoverElement() /*-{
-    return this.display.mover;
+  public final native Element mover() /*-{
+    return this.display.mover
   }-*/;
 
-  public final native Element getMeasureElement() /*-{
-    return this.display.measure;
+  public final native Element measure() /*-{
+    return this.display.measure
   }-*/;
 
-  public final native Element getScrollbarV() /*-{
-    return this.display.scrollbarV;
+  public final native Element scrollbarV() /*-{
+    return this.display.scrollbarV
   }-*/;
 
   public static final native KeyMap cloneKeyMap(String name) /*-{
@@ -312,36 +305,31 @@
   }-*/;
 
   public final native void execCommand(String cmd) /*-{
-    this.execCommand(cmd);
+    this.execCommand(cmd)
   }-*/;
 
   public static final native void addKeyMap(String name, KeyMap km) /*-{
-    $wnd.CodeMirror.keyMap[name] = km;
+    $wnd.CodeMirror.keyMap[name] = km
   }-*/;
 
-  public static final native void handleVimKey(CodeMirror cm, String key) /*-{
-    $wnd.CodeMirror.Vim.handleKey(cm, key);
-  }-*/;
-
-  public static final native void mapVimKey(String alias, String actual) /*-{
-    $wnd.CodeMirror.Vim.map(alias, actual);
-  }-*/;
-
-  public final native boolean hasVimSearchHighlight() /*-{
-    return this.state.vim && this.state.vim.searchState_ &&
-        !!this.state.vim.searchState_.getOverlay();
+  public final native Vim vim() /*-{
+    return this;
   }-*/;
 
   public final native DisplaySide side() /*-{ return this._sbs2_side }-*/;
+  public final native CodeMirror side(DisplaySide side) /*-{
+    this._sbs2_side = side;
+    return this;
+  }-*/;
 
   protected CodeMirror() {
   }
 
   public static class Viewport extends JavaScriptObject {
-    public final native int getFrom() /*-{ return this.from; }-*/;
-    public final native int getTo() /*-{ return this.to; }-*/;
+    public final native int from() /*-{ return this.from }-*/;
+    public final native int to() /*-{ return this.to }-*/;
     public final boolean contains(int line) {
-      return getFrom() <= line && line < getTo();
+      return from() <= line && line < to();
     }
 
     protected Viewport() {
@@ -353,6 +341,11 @@
     }
   }
 
+  public static class RegisteredHandler extends JavaScriptObject {
+    protected RegisteredHandler() {
+    }
+  }
+
   public interface EventHandler {
     public void handle(CodeMirror instance, NativeEvent event);
   }
@@ -367,10 +360,6 @@
   }
 
   public interface BeforeSelectionChangeHandler {
-    public void handle(CodeMirror instance, LineCharacter anchor, LineCharacter head);
+    public void handle(CodeMirror instance, Pos anchor, Pos head);
   }
-
-  public final native String getValue() /*-{
-    return this.getValue();
-  }-*/;
 }
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirrorDoc.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirrorDoc.java
index ff5d230..d04cc24 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirrorDoc.java
+++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirrorDoc.java
@@ -20,11 +20,11 @@
 public class CodeMirrorDoc extends JavaScriptObject {
 
   public final native void replaceRange(String replacement,
-      LineCharacter from, LineCharacter to) /*-{
+      Pos from, Pos to) /*-{
     this.replaceRange(replacement, from, to);
   }-*/;
 
-  public final native void insertText(String insertion, LineCharacter at) /*-{
+  public final native void insertText(String insertion, Pos at) /*-{
     this.replaceRange(insertion, at);
   }-*/;
 
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/Lib.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/Lib.java
index bb60fe9..2259467 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/Lib.java
+++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/Lib.java
@@ -23,10 +23,10 @@
 interface Lib extends ClientBundle {
   static final Lib I = GWT.create(Lib.class);
 
-  @Source("cm3.css")
+  @Source("cm4.css")
   ExternalTextResource css();
 
-  @Source("cm3.js")
+  @Source("cm4.js")
   @DoNotEmbed
   DataResource js();
 }
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/LineCharacter.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/LineCharacter.java
deleted file mode 100644
index 5076aff..0000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/LineCharacter.java
+++ /dev/null
@@ -1,44 +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 net.codemirror.lib;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-/** {line, ch} objects used within CodeMirror. */
-public class LineCharacter extends JavaScriptObject {
-  public static LineCharacter create(int line, int ch) {
-    return createImpl(line, ch);
-  }
-
-  public static LineCharacter create(int line) {
-    return createImpl(line, 0);
-  }
-
-  private static LineCharacter createImpl(int line, int ch) {
-    LineCharacter lineCh = createObject().cast();
-    lineCh.setLine(line);
-    lineCh.setCh(ch);
-    return lineCh;
-  }
-
-  public final native void setLine(int line) /*-{ this.line = line; }-*/;
-  public final native void setCh(int ch) /*-{ this.ch = ch; }-*/;
-
-  public final native int getLine() /*-{ return this.line; }-*/;
-  public final native int getCh() /*-{ return this.ch; }-*/;
-
-  protected LineCharacter() {
-  }
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/LineWidget.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/LineWidget.java
index 91547fb..62c219b 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/LineWidget.java
+++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/LineWidget.java
@@ -18,17 +18,13 @@
 
 /** LineWidget objects used within CodeMirror. */
 public class LineWidget extends JavaScriptObject {
-  public static LineWidget create() {
-    return createObject().cast();
-  }
-
-  public final native void clear() /*-{ this.clear(); }-*/;
-  public final native void changed() /*-{ this.changed(); }-*/;
+  public final native void clear() /*-{ this.clear() }-*/;
+  public final native void changed() /*-{ this.changed() }-*/;
 
   public final native void onRedraw(Runnable thunk) /*-{
     this.on("redraw", $entry(function() {
       thunk.@java.lang.Runnable::run()();
-    }));
+    }))
   }-*/;
 
   public final native void onFirstRedraw(Runnable thunk) /*-{
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/Loader.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/Loader.java
index 4d894c6..bae54d4 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/Loader.java
+++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/Loader.java
@@ -41,7 +41,7 @@
       injectScript(Lib.I.js().getSafeUri(), new GerritCallback<Void>(){
         @Override
         public void onSuccess(Void result) {
-          initVimKeys();
+          Vim.initKeyMap();
           cb.onSuccess(null);
         }
       });
@@ -87,28 +87,6 @@
       .cast();
   }
 
-  private static void initVimKeys() {
-    // TODO: Better custom keybindings, remove temporary navigation hacks.
-    KeyMap km = CodeMirror.cloneKeyMap("vim");
-    for (String s : new String[] {
-        "A", "C", "I", "O", "R", "U",
-        "Ctrl-C", "Ctrl-O", "Ctrl-P", "Ctrl-S",
-        "Ctrl-F", "Ctrl-B", "Ctrl-R"}) {
-      km.remove(s);
-    }
-    for (int i = 0; i <= 9; i++) {
-      km.remove("Ctrl-" + i);
-    }
-    CodeMirror.addKeyMap("vim_ro", km);
-
-    CodeMirror.mapVimKey("j", "gj");
-    CodeMirror.mapVimKey("k", "gk");
-    CodeMirror.mapVimKey("Down", "gj");
-    CodeMirror.mapVimKey("Up", "gk");
-    CodeMirror.mapVimKey("<PageUp>", "<C-u>");
-    CodeMirror.mapVimKey("<PageDown>", "<C-d>");
-  }
-
   private static void error(Exception e) {
     Logger log = Logger.getLogger("net.codemirror");
     log.log(Level.SEVERE, "Cannot load portions of CodeMirror", e);
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/ModeInjector.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/ModeInjector.java
index cfb098e..60490f9 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/ModeInjector.java
+++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/ModeInjector.java
@@ -43,11 +43,13 @@
     DataResource[] all = {
       Modes.I.clike(),
       Modes.I.clojure(),
-      Modes.I.commonlisp(),
       Modes.I.coffeescript(),
+      Modes.I.commonlisp(),
       Modes.I.css(),
       Modes.I.d(),
+      Modes.I.dart(),
       Modes.I.diff(),
+      Modes.I.dockerfile(),
       Modes.I.dtd(),
       Modes.I.erlang(),
       Modes.I.gas(),
@@ -65,11 +67,14 @@
       Modes.I.properties(),
       Modes.I.python(),
       Modes.I.r(),
+      Modes.I.rst(),
       Modes.I.ruby(),
       Modes.I.scheme(),
       Modes.I.shell(),
       Modes.I.smalltalk(),
+      Modes.I.soy(),
       Modes.I.sql(),
+      Modes.I.stex(),
       Modes.I.velocity(),
       Modes.I.verilog(),
       Modes.I.xml(),
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/Pos.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/Pos.java
new file mode 100644
index 0000000..07ead43
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/Pos.java
@@ -0,0 +1,41 @@
+// 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 net.codemirror.lib;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+/** Pos (or {line, ch}) objects used within CodeMirror. */
+public class Pos extends JavaScriptObject {
+  public static final native Pos create(int line) /*-{
+    return $wnd.CodeMirror.Pos(line)
+  }-*/;
+
+  public static final native Pos create(int line, int ch) /*-{
+    return $wnd.CodeMirror.Pos(line, ch)
+  }-*/;
+
+  public final native void line(int l) /*-{ this.line = l }-*/;
+  public final native void ch(int c) /*-{ this.ch = c }-*/;
+
+  public final native int line() /*-{ return this.line }-*/;
+  public final native int ch() /*-{ return this.ch || 0 }-*/;
+
+  public final boolean equals(Pos o) {
+    return this == o || (line() == o.line() && ch() == o.ch());
+  }
+
+  protected Pos() {
+  }
+}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/ScrollInfo.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/ScrollInfo.java
index 096f1ad..2623530 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/ScrollInfo.java
+++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/ScrollInfo.java
@@ -18,20 +18,20 @@
 
 /** Returned by {@link CodeMirror#getScrollInfo()}. */
 public class ScrollInfo extends JavaScriptObject {
-  public final native double getLeft() /*-{ return this.left; }-*/;
-  public final native double getTop() /*-{ return this.top; }-*/;
+  public final native double left() /*-{ return this.left }-*/;
+  public final native double top() /*-{ return this.top }-*/;
 
   /**
    * Pixel height of the full content being scrolled. This may only be an
    * estimate given by CodeMirror. Line widgets further down in the document may
    * not be measured, so line heights can be incorrect until drawn.
    */
-  public final native double getHeight() /*-{ return this.height; }-*/;
-  public final native double getWidth() /*-{ return this.width; }-*/;
+  public final native double height() /*-{ return this.height }-*/;
+  public final native double width() /*-{ return this.width }-*/;
 
   /** Visible height of the viewport, excluding scrollbars. */
-  public final native double getClientHeight() /*-{ return this.clientHeight; }-*/;
-  public final native double getClientWidth() /*-{ return this.clientWidth; }-*/;
+  public final native double clientHeight() /*-{ return this.clientHeight }-*/;
+  public final native double clientWidth() /*-{ return this.clientWidth }-*/;
 
   protected ScrollInfo() {
   }
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/TextMarker.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/TextMarker.java
index 50db13c..2d69015 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/TextMarker.java
+++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/TextMarker.java
@@ -19,10 +19,6 @@
 
 /** Object that represents a text marker within CodeMirror */
 public class TextMarker extends JavaScriptObject {
-  public static TextMarker create() {
-    return createObject().cast();
-  }
-
   public final native void clear() /*-{ this.clear(); }-*/;
   public final native void changed() /*-{ this.changed(); }-*/;
   public final native FromTo find() /*-{ return this.find(); }-*/;
@@ -33,24 +29,21 @@
   }
 
   public static class FromTo extends JavaScriptObject {
-    public static FromTo create(LineCharacter from, LineCharacter to) {
-      FromTo fromTo = createObject().cast();
-      fromTo.setFrom(from);
-      fromTo.setTo(to);
-      return fromTo;
-    }
+    public static final native FromTo create(Pos f, Pos t) /*-{
+      return {from: f, to: t}
+    }-*/;
 
     public static FromTo create(CommentRange range) {
       return create(
-          LineCharacter.create(range.start_line() - 1, range.start_character()),
-          LineCharacter.create(range.end_line() - 1, range.end_character()));
+          Pos.create(range.start_line() - 1, range.start_character()),
+          Pos.create(range.end_line() - 1, range.end_character()));
     }
 
-    public final native LineCharacter getFrom() /*-{ return this.from; }-*/;
-    public final native LineCharacter getTo() /*-{ return this.to; }-*/;
+    public final native Pos from() /*-{ return this.from }-*/;
+    public final native Pos to() /*-{ return this.to }-*/;
 
-    public final native void setFrom(LineCharacter from) /*-{ this.from = from; }-*/;
-    public final native void setTo(LineCharacter to) /*-{ this.to = to; }-*/;
+    public final native void from(Pos f) /*-{ this.from = f }-*/;
+    public final native void to(Pos t) /*-{ this.to = t }-*/;
 
     protected FromTo() {
     }
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/Vim.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/Vim.java
new file mode 100644
index 0000000..e7da469
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/Vim.java
@@ -0,0 +1,64 @@
+// Copyright (C) 2014 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 net.codemirror.lib;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+/**
+ * Glue around the Vim emulation for {@link CodeMirror}.
+ *
+ * As an instance {@code this} is actually the {@link CodeMirror} object. Class
+ * Vim is providing a new namespace for Vim related methods that are associated
+ * with an editor.
+ */
+public class Vim extends JavaScriptObject {
+  static void initKeyMap() {
+    // TODO: Better custom keybindings, remove temporary navigation hacks.
+    KeyMap km = CodeMirror.cloneKeyMap("vim");
+    for (String s : new String[] {
+        "A", "C", "I", "O", "R", "U",
+        "Ctrl-C", "Ctrl-O", "Ctrl-P", "Ctrl-S",
+        "Ctrl-F", "Ctrl-B", "Ctrl-R"}) {
+      km.remove(s);
+    }
+    for (int i = 0; i <= 9; i++) {
+      km.remove("Ctrl-" + i);
+    }
+    CodeMirror.addKeyMap("vim_ro", km);
+
+    mapKey("j", "gj");
+    mapKey("k", "gk");
+    mapKey("Down", "gj");
+    mapKey("Up", "gk");
+    mapKey("<PageUp>", "<C-u>");
+    mapKey("<PageDown>", "<C-d>");
+  }
+
+  public static final native void mapKey(String alias, String actual) /*-{
+    $wnd.CodeMirror.Vim.map(alias, actual)
+  }-*/;
+
+  public final native void handleKey(String key) /*-{
+    $wnd.CodeMirror.Vim.handleKey(this, key)
+  }-*/;
+
+  public final native boolean hasSearchHighlight() /*-{
+    var v = this.state.vim;
+    return v && v.searchState_ && !!v.searchState_.getOverlay();
+  }-*/;
+
+  protected Vim() {
+  }
+}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/mode/Modes.java b/gerrit-gwtui/src/main/java/net/codemirror/mode/Modes.java
index 9b56c38..13f2825 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/mode/Modes.java
+++ b/gerrit-gwtui/src/main/java/net/codemirror/mode/Modes.java
@@ -24,40 +24,45 @@
   public static final Modes I = GWT.create(Modes.class);
 
   @Source("mode_map") TextResource mode_map();
-  @Source("clike/clike.js") @DoNotEmbed DataResource clike();
-  @Source("clojure/clojure.js") @DoNotEmbed DataResource clojure();
-  @Source("commonlisp/commonlisp.js") @DoNotEmbed DataResource commonlisp();
-  @Source("coffeescript/coffeescript.js") @DoNotEmbed DataResource coffeescript();
-  @Source("css/css.js") @DoNotEmbed DataResource css();
-  @Source("d/d.js") @DoNotEmbed DataResource d();
-  @Source("diff/diff.js") @DoNotEmbed DataResource diff();
-  @Source("dtd/dtd.js") @DoNotEmbed DataResource dtd();
-  @Source("erlang/erlang.js") @DoNotEmbed DataResource erlang();
-  @Source("gas/gas.js") @DoNotEmbed DataResource gas();
+  @Source("clike.js") @DoNotEmbed DataResource clike();
+  @Source("clojure.js") @DoNotEmbed DataResource clojure();
+  @Source("coffeescript.js") @DoNotEmbed DataResource coffeescript();
+  @Source("commonlisp.js") @DoNotEmbed DataResource commonlisp();
+  @Source("css.js") @DoNotEmbed DataResource css();
+  @Source("d.js") @DoNotEmbed DataResource d();
+  @Source("dart.js") @DoNotEmbed DataResource dart();
+  @Source("diff.js") @DoNotEmbed DataResource diff();
+  @Source("dockerfile.js") @DoNotEmbed DataResource dockerfile();
+  @Source("dtd.js") @DoNotEmbed DataResource dtd();
+  @Source("erlang.js") @DoNotEmbed DataResource erlang();
+  @Source("gas.js") @DoNotEmbed DataResource gas();
   @Source("gerrit/commit.js") @DoNotEmbed DataResource gerrit_commit();
-  @Source("gfm/gfm.js") @DoNotEmbed DataResource gfm();
-  @Source("groovy/groovy.js") @DoNotEmbed DataResource groovy();
-  @Source("haskell/haskell.js") @DoNotEmbed DataResource haskell();
-  @Source("htmlmixed/htmlmixed.js") @DoNotEmbed DataResource htmlmixed();
-  @Source("javascript/javascript.js") @DoNotEmbed DataResource javascript();
-  @Source("lua/lua.js") @DoNotEmbed DataResource lua();
-  @Source("markdown/markdown.js") @DoNotEmbed DataResource markdown();
-  @Source("perl/perl.js") @DoNotEmbed DataResource perl();
-  @Source("php/php.js") @DoNotEmbed DataResource php();
-  @Source("pig/pig.js") @DoNotEmbed DataResource pig();
-  @Source("properties/properties.js") @DoNotEmbed DataResource properties();
-  @Source("python/python.js") @DoNotEmbed DataResource python();
-  @Source("r/r.js") @DoNotEmbed DataResource r();
-  @Source("ruby/ruby.js") @DoNotEmbed DataResource ruby();
-  @Source("scheme/scheme.js") @DoNotEmbed DataResource scheme();
-  @Source("shell/shell.js") @DoNotEmbed DataResource shell();
-  @Source("smalltalk/smalltalk.js") @DoNotEmbed DataResource smalltalk();
-  @Source("sql/sql.js") @DoNotEmbed DataResource sql();
-  @Source("tcl/tcl.js") @DoNotEmbed DataResource tcl();
-  @Source("velocity/velocity.js") @DoNotEmbed DataResource velocity();
-  @Source("verilog/verilog.js") @DoNotEmbed DataResource verilog();
-  @Source("xml/xml.js") @DoNotEmbed DataResource xml();
-  @Source("yaml/yaml.js") @DoNotEmbed DataResource yaml();
+  @Source("gfm.js") @DoNotEmbed DataResource gfm();
+  @Source("groovy.js") @DoNotEmbed DataResource groovy();
+  @Source("haskell.js") @DoNotEmbed DataResource haskell();
+  @Source("htmlmixed.js") @DoNotEmbed DataResource htmlmixed();
+  @Source("javascript.js") @DoNotEmbed DataResource javascript();
+  @Source("lua.js") @DoNotEmbed DataResource lua();
+  @Source("markdown.js") @DoNotEmbed DataResource markdown();
+  @Source("perl.js") @DoNotEmbed DataResource perl();
+  @Source("php.js") @DoNotEmbed DataResource php();
+  @Source("pig.js") @DoNotEmbed DataResource pig();
+  @Source("properties.js") @DoNotEmbed DataResource properties();
+  @Source("python.js") @DoNotEmbed DataResource python();
+  @Source("r.js") @DoNotEmbed DataResource r();
+  @Source("rst.js") @DoNotEmbed DataResource rst();
+  @Source("ruby.js") @DoNotEmbed DataResource ruby();
+  @Source("scheme.js") @DoNotEmbed DataResource scheme();
+  @Source("shell.js") @DoNotEmbed DataResource shell();
+  @Source("smalltalk.js") @DoNotEmbed DataResource smalltalk();
+  @Source("soy.js") @DoNotEmbed DataResource soy();
+  @Source("sql.js") @DoNotEmbed DataResource sql();
+  @Source("stex.js") @DoNotEmbed DataResource stex();
+  @Source("tcl.js") @DoNotEmbed DataResource tcl();
+  @Source("velocity.js") @DoNotEmbed DataResource velocity();
+  @Source("verilog.js") @DoNotEmbed DataResource verilog();
+  @Source("xml.js") @DoNotEmbed DataResource xml();
+  @Source("yaml.js") @DoNotEmbed DataResource yaml();
 
   // When adding a resource, update static initializer in ModeInjector.
 }
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/mode/gerrit/commit.js b/gerrit-gwtui/src/main/java/net/codemirror/mode/gerrit/commit.js
index a846e50..e1fe898 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/mode/gerrit/commit.js
+++ b/gerrit-gwtui/src/main/java/net/codemirror/mode/gerrit/commit.js
@@ -2,7 +2,7 @@
   var header = /^(Parent|Author|AuthorDate|Commit|CommitDate):/;
   var id = /^Change-Id: I[0-9a-f]{40}/;
   var footer = /^[A-Z][A-Za-z0-9-]+:/;
-  var sha1 = /[0-9a-f]{6,40}/;
+  var sha1 = /\b[0-9a-f]{6,40}/;
 
   return {
     token: function(stream) {
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/mode/mode_map b/gerrit-gwtui/src/main/java/net/codemirror/mode/mode_map
index 2bff364..ac7b52d 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/mode/mode_map
+++ b/gerrit-gwtui/src/main/java/net/codemirror/mode/mode_map
@@ -1,12 +1,15 @@
 clike:
-text/x-csrc
 text/x-c
 text/x-chdr
+text/x-csharp
+text/x-csrc
 text/x-c++src
 text/x-c++hdr
 text/x-java
-text/x-csharp
+text/x-objectivec
 text/x-scala
+x-shader/x-fragment
+x-shader/x-vertex
 
 clojure:
 text/x-clojure
@@ -24,9 +27,15 @@
 d:
 text/x-d
 
+dart:
+application/dart
+
 diff:
 text/x-diff
 
+dockerfile:
+text/x-dockerfile
+
 dtd:
 application/xml-dtd
 
@@ -94,6 +103,9 @@
 r:
 text/r-src
 
+rst:
+text/x-rst
+
 ruby:
 text/x-ruby
 
@@ -107,12 +119,18 @@
 smalltalk:
 text/x-stsrc
 
+soy:
+text/x-soy
+
 sql:
 text/x-sql
 text/x-mariadb
 text/x-mysql
 text/x-plsql
 
+stex:
+text/x-stex
+
 tcl:
 text/x-tcl
 
diff --git a/gerrit-gwtui/src/test/java/com/google/gerrit/client/diff/EditIteratorTest.java b/gerrit-gwtui/src/test/java/com/google/gerrit/client/diff/EditIteratorTest.java
index f759306..d751f34 100644
--- a/gerrit-gwtui/src/test/java/com/google/gerrit/client/diff/EditIteratorTest.java
+++ b/gerrit-gwtui/src/test/java/com/google/gerrit/client/diff/EditIteratorTest.java
@@ -22,7 +22,7 @@
 import com.googlecode.gwt.test.GwtModule;
 import com.googlecode.gwt.test.GwtTest;
 
-import net.codemirror.lib.LineCharacter;
+import net.codemirror.lib.Pos;
 
 import org.junit.Before;
 import org.junit.Ignore;
@@ -35,8 +35,8 @@
 public class EditIteratorTest extends GwtTest {
   private JsArrayString lines;
 
-  private void assertLineChsEqual(LineCharacter a, LineCharacter b) {
-    assertEquals(a.getLine() + "," + a.getCh(), b.getLine() + "," + b.getCh());
+  private void assertLineChsEqual(Pos a, Pos b) {
+    assertEquals(a.line() + "," + a.ch(), b.line() + "," + b.ch());
   }
 
   @Before
@@ -50,57 +50,57 @@
   @Test
   public void testNegativeAdvance() {
     EditIterator i = new EditIterator(lines, 0);
-    assertLineChsEqual(LineCharacter.create(1, 1), i.advance(5));
-    assertLineChsEqual(LineCharacter.create(0, 3), i.advance(-2));
+    assertLineChsEqual(Pos.create(1, 1), i.advance(5));
+    assertLineChsEqual(Pos.create(0, 3), i.advance(-2));
   }
 
   @Test
   public void testNoAdvance() {
     EditIterator iter = new EditIterator(lines, 0);
-    assertLineChsEqual(LineCharacter.create(0), iter.advance(0));
+    assertLineChsEqual(Pos.create(0), iter.advance(0));
   }
 
   @Test
   public void testSimpleAdvance() {
     EditIterator iter = new EditIterator(lines, 0);
-    assertLineChsEqual(LineCharacter.create(0, 1), iter.advance(1));
+    assertLineChsEqual(Pos.create(0, 1), iter.advance(1));
   }
 
   @Test
   public void testEndsBeforeNewline() {
     EditIterator iter = new EditIterator(lines, 0);
-    assertLineChsEqual(LineCharacter.create(0, 3), iter.advance(3));
+    assertLineChsEqual(Pos.create(0, 3), iter.advance(3));
   }
 
   @Test
   public void testEndsOnNewline() {
     EditIterator iter = new EditIterator(lines, 0);
-    assertLineChsEqual(LineCharacter.create(1), iter.advance(4));
+    assertLineChsEqual(Pos.create(1), iter.advance(4));
   }
 
   @Test
   public void testAcrossNewline() {
     EditIterator iter = new EditIterator(lines, 0);
-    assertLineChsEqual(LineCharacter.create(1, 1), iter.advance(5));
+    assertLineChsEqual(Pos.create(1, 1), iter.advance(5));
   }
 
   @Test
   public void testContinueFromBeforeNewline() {
     EditIterator iter = new EditIterator(lines, 0);
     iter.advance(3);
-    assertLineChsEqual(LineCharacter.create(2, 2), iter.advance(7));
+    assertLineChsEqual(Pos.create(2, 2), iter.advance(7));
   }
 
   @Test
   public void testContinueFromAfterNewline() {
     EditIterator iter = new EditIterator(lines, 0);
     iter.advance(4);
-    assertLineChsEqual(LineCharacter.create(2, 2), iter.advance(6));
+    assertLineChsEqual(Pos.create(2, 2), iter.advance(6));
   }
 
   @Test
   public void testAcrossMultipleLines() {
     EditIterator iter = new EditIterator(lines, 0);
-    assertLineChsEqual(LineCharacter.create(2, 2), iter.advance(10));
+    assertLineChsEqual(Pos.create(2, 2), iter.advance(10));
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
index 11f2e91..14c224f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
@@ -62,7 +62,7 @@
     child(ACCOUNT_KIND, "capabilities").to(Capabilities.class);
     get(ACCOUNT_KIND, "groups").to(GetGroups.class);
     get(ACCOUNT_KIND, "preferences").to(GetPreferences.class);
-    post(ACCOUNT_KIND, "preferences").to(SetPreferences.class);
+    put(ACCOUNT_KIND, "preferences").to(SetPreferences.class);
     get(ACCOUNT_KIND, "preferences.diff").to(GetDiffPreferences.class);
     put(ACCOUNT_KIND, "preferences.diff").to(SetDiffPreferences.class);
     get(CAPABILITY_KIND).to(GetCapabilities.CheckOne.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRelated.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRelated.java
index 077ec6f1..6056e5f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRelated.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRelated.java
@@ -20,11 +20,13 @@
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.extensions.common.ChangeStatus;
 import com.google.gerrit.extensions.common.CommitInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.PatchSetAncestor;
+import com.google.gerrit.reviewdb.client.RevId;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CommonConverters;
 import com.google.gerrit.server.git.GitRepositoryManager;
@@ -119,6 +121,18 @@
       if (p != null) {
         g = changes.get(p.getId().getParentKey());
         added.add(p.getId().getParentKey());
+      } else {
+        // check if there is a merged or abandoned change for this commit
+        ReviewDb db = dbProvider.get();
+        for (PatchSet ps : db.patchSets().byRevision(new RevId(c.name())).toList()) {
+          Change change = db.changes().get(ps.getId().getParentKey());
+          if (change != null && change.getDest().equals(rsrc.getChange().getDest())) {
+            p = ps;
+            g = change;
+            added.add(g.getId());
+            break;
+          }
+        }
       }
       parents.add(new ChangeAndCommit(g, p, c));
     }
@@ -278,6 +292,7 @@
 
   public static class ChangeAndCommit {
     public String changeId;
+    public ChangeStatus status;
     public CommitInfo commit;
     public Integer _changeNumber;
     public Integer _revisionNumber;
@@ -286,6 +301,7 @@
     ChangeAndCommit(@Nullable Change change, @Nullable PatchSet ps, RevCommit c) {
       if (change != null) {
         changeId = change.getKey().get();
+        status = change.getStatus().asChangeStatus();
         _changeNumber = change.getChangeId();
         _revisionNumber = ps != null ? ps.getPatchSetId() : null;
         PatchSet.Id curr = change.currentPatchSetId();
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mime-types.properties b/gerrit-server/src/main/resources/com/google/gerrit/server/mime-types.properties
index 817790f..a2b6770 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mime-types.properties
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mime-types.properties
@@ -7,12 +7,16 @@
 cs = text/x-csharp
 cxx = text/x-c++src
 d = text/x-d
+dart = application/dart
 defs = text/x-python
 diff = text/x-diff
+Dockerfile = text/x-dockerfile
 dtd = application/xml-dtd
 el = text/x-common-lisp
 erl = text/x-erlang
+frag = x-shader/x-fragment
 gitmodules = text/x-ini
+glsl = x-shader/x-vertex
 go = text/x-go
 groovy = text/x-groovy
 hs = text/x-haskell
@@ -20,6 +24,7 @@
 lisp = text/x-common-lisp
 lsp = text/x-common-lisp
 lua = text/x-lua
+m = text/x-objectivec
 md = text/x-markdown
 patch = text/x-diff
 php = text/x-php
@@ -31,9 +36,13 @@
 py = text/x-python
 r = text/r-src
 rb = text/x-ruby
+rst = text/x-rst
 scala = text/x-scala
+soy = text/x-soy
 st = text/x-stsrc
+stex = text/x-stex
 v = text/x-verilog
+vert = x-shader/x-vertex
 vh = text/x-verilog
 vm = text/velocity
 yaml = text/x-yaml
diff --git a/lib/codemirror/BUCK b/lib/codemirror/BUCK
index 1f79e6e..213fbe3 100644
--- a/lib/codemirror/BUCK
+++ b/lib/codemirror/BUCK
@@ -1,9 +1,9 @@
 include_defs('//lib/maven.defs')
-include_defs('//lib/codemirror/cm3.defs')
+include_defs('//lib/codemirror/cm.defs')
 include_defs('//lib/codemirror/closure.defs')
 
-VERSION = '57e7ed7177'
-SHA1 = 'd78bc5518707960647d0b8b85d9f1ac011b785d5'
+VERSION = '4.10'
+SHA1 = '577730c11277bf96ce637b183409a2865593f76b'
 URL = GERRIT + 'net/codemirror/codemirror-%s.zip' % VERSION
 
 ZIP = 'codemirror-%s.zip' % VERSION
@@ -25,38 +25,58 @@
       "echo '*/' >>$OUT",
     ] +
     ['unzip -p $(location :zip) %s/%s >>$OUT' % (TOP, n)
-     for n in CM3_CSS + CM3_THEMES]
+     for n in CM_CSS + CM_THEMES]
   ),
   deps = [':zip'],
-  out = 'cm3.css',
+  out = 'cm4.css',
 )
 
 genrule(
-  name = 'cm3-verbose',
+  name = 'cm-verbose',
   cmd = ';'.join([
-      ':>$OUT',
-      "echo '/** @license' >>$OUT",
+      "echo '/** @license' >$OUT",
       'unzip -p $(location :zip) %s/LICENSE >>$OUT' % TOP,
       "echo '*/' >>$OUT",
     ] +
-    ['unzip -p $(location :zip) %s/%s >>$OUT' % (TOP, n)
-     for n in CM3_JS]
+    ['unzip -p $(location :zip) %s/%s >>$OUT' % (TOP, n) for n in CM_JS]
   ),
   deps = [':zip'],
-  out = 'cm3-verbose.js',
+  out = 'cm4-verbose.js',
 )
 
 js_minify(
   name = 'js',
-  generated = [':cm3-verbose'],
+  generated = [':cm-verbose'],
   compiler_args = CLOSURE_COMPILER_ARGS,
-  out = 'cm3.js'
+  out = 'cm4.js'
 )
 
+for n in CM_MODES:
+  genrule (
+    name = 'mode_%s_src' % n,
+    cmd = ';'.join([
+      "echo '/** @license' >$OUT",
+      'unzip -p $(location :zip) %s/LICENSE >>$OUT' % TOP,
+      "echo '*/' >>$OUT",
+      'unzip -p $(location :zip) %s/mode/%s/%s.js >>$OUT' % (TOP, n, n),
+      ]),
+    deps = [':zip'],
+    out = 'mode_%s_src.js' %n,
+  )
+  js_minify(
+    name = 'mode_%s_js' % n,
+    generated = [':mode_%s_src' % n],
+    compiler_args = CLOSURE_COMPILER_ARGS,
+    out = 'mode_%s.js' % n,
+  )
+
 prebuilt_jar(
   name = 'codemirror',
   binary_jar = ':jar',
-  deps = ['//lib:LICENSE-codemirror'],
+  deps = [
+    ':jar',
+    '//lib:LICENSE-codemirror',
+  ],
   visibility = ['PUBLIC'],
 )
 
@@ -64,20 +84,18 @@
   name = 'jar',
   cmd = ';'.join([
     'cd $TMP',
-    'unzip -q $(location :zip) %s' %
-    ' '.join(['%s/mode/%s' % (TOP, n) for n in CM3_MODES]),
-    ';'.join(['$(exe :js_minifier) ' +
-    ' '.join(CLOSURE_COMPILER_ARGS) +
-    ' --js_output_file %s/mode/%s.min --js %s/mode/%s'
-    % (TOP, n, TOP, n) for n in CM3_MODES]),
-    ';'.join(['mv %s/mode/%s.min %s/mode/%s' % (TOP, n, TOP, n) for n in CM3_MODES]),
-    'mkdir net',
-    'mv %s net/codemirror' % TOP,
-    'mkdir net/codemirror/lib',
+    'mkdir -p net/codemirror/{lib,mode}',
     'cp $(location :css) net/codemirror/lib',
-    'cp $(location :js) net/codemirror/lib',
-    'zip -qr $OUT *'
-  ]),
+    'cp $(location :js) net/codemirror/lib']
+    + ['cp $(location :mode_%s_js) net/codemirror/mode/%s.js' % (n, n)
+       for n in CM_MODES]
+    + ['zip -qr $OUT net/codemirror/{lib,mode}']),
+  deps = [
+    ':css',
+    ':js',
+    ':js_minifier',
+    ':zip',
+  ] + [':mode_%s_js' % n for n in CM_MODES],
   out = 'codemirror.jar',
 )
 
@@ -87,6 +105,7 @@
     ' -o $OUT' +
     ' -u ' + URL +
     ' -v ' + SHA1,
+  deps = ['//tools:download_file'],
   out = ZIP,
 )
 
diff --git a/lib/codemirror/closure.defs b/lib/codemirror/closure.defs
index 08b6897..e5dbc40 100644
--- a/lib/codemirror/closure.defs
+++ b/lib/codemirror/closure.defs
@@ -3,7 +3,8 @@
     out,
     compiler_args = [],
     srcs = [],
-    generated = []):
+    generated = [],
+    deps = []):
   cmd = ['$(exe :js_minifier) --js_output_file $OUT'] + compiler_args
   if srcs:
     cmd.append('$SRCS')
@@ -14,5 +15,6 @@
     name = name,
     cmd = ' '.join(cmd),
     srcs = srcs,
+    deps = deps + generated + [':js_minifier'],
     out = out,
-)
+  )
diff --git a/lib/codemirror/cm.defs b/lib/codemirror/cm.defs
new file mode 100644
index 0000000..eaba040
--- /dev/null
+++ b/lib/codemirror/cm.defs
@@ -0,0 +1,68 @@
+CM_CSS = [
+  'lib/codemirror.css',
+  'addon/dialog/dialog.css',
+  'addon/scroll/simplescrollbars.css',
+]
+
+CM_THEMES = [
+  'theme/eclipse.css',
+  'theme/elegant.css',
+  'theme/midnight.css',
+  'theme/neat.css',
+  'theme/night.css',
+  'theme/twilight.css',
+]
+
+CM_JS = [
+  'lib/codemirror.js',
+  'keymap/vim.js',
+  'addon/dialog/dialog.js',
+  'addon/scroll/simplescrollbars.js',
+  'addon/search/searchcursor.js',
+  'addon/search/search.js',
+  'addon/selection/mark-selection.js',
+  'addon/edit/trailingspace.js',
+  'addon/mode/overlay.js',
+  'addon/mode/simple.js',
+]
+
+CM_MODES = [
+  'clike',
+  'clojure',
+  'coffeescript',
+  'commonlisp',
+  'css',
+  'd',
+  'dart',
+  'diff',
+  'dockerfile',
+  'dtd',
+  'erlang',
+  'gas',
+  'gfm',
+  'groovy',
+  'haskell',
+  'htmlmixed',
+  'javascript',
+  'lua',
+  'markdown',
+  'perl',
+  'php',
+  'pig',
+  'properties',
+  'python',
+  'r',
+  'rst',
+  'ruby',
+  'scheme',
+  'shell',
+  'smalltalk',
+  'soy',
+  'sql',
+  'stex',
+  'tcl',
+  'velocity',
+  'verilog',
+  'xml',
+  'yaml',
+]
diff --git a/lib/codemirror/cm3.defs b/lib/codemirror/cm3.defs
deleted file mode 100644
index e9eff39..0000000
--- a/lib/codemirror/cm3.defs
+++ /dev/null
@@ -1,59 +0,0 @@
-CM3_CSS = [
-  'lib/codemirror.css',
-  'addon/dialog/dialog.css',
-]
-
-CM3_THEMES = [
-  'theme/eclipse.css',
-  'theme/elegant.css',
-  'theme/midnight.css',
-  'theme/neat.css',
-  'theme/night.css',
-  'theme/twilight.css',
-]
-
-CM3_JS = [
-  'lib/codemirror.js',
-  'keymap/vim.js',
-  'addon/dialog/dialog.js',
-  'addon/search/searchcursor.js',
-  'addon/search/search.js',
-  'addon/selection/mark-selection.js',
-  'addon/edit/trailingspace.js',
-]
-
-CM3_MODES = [
-  'clike/clike.js',
-  'clojure/clojure.js',
-  'coffeescript/coffeescript.js',
-  'commonlisp/commonlisp.js',
-  'css/css.js',
-  'd/d.js',
-  'diff/diff.js',
-  'dtd/dtd.js',
-  'erlang/erlang.js',
-  'gas/gas.js',
-  'gfm/gfm.js',
-  'groovy/groovy.js',
-  'haskell/haskell.js',
-  'htmlmixed/htmlmixed.js',
-  'javascript/javascript.js',
-  'lua/lua.js',
-  'markdown/markdown.js',
-  'perl/perl.js',
-  'php/php.js',
-  'pig/pig.js',
-  'properties/properties.js',
-  'python/python.js',
-  'r/r.js',
-  'ruby/ruby.js',
-  'scheme/scheme.js',
-  'shell/shell.js',
-  'smalltalk/smalltalk.js',
-  'sql/sql.js',
-  'tcl/tcl.js',
-  'velocity/velocity.js',
-  'verilog/verilog.js',
-  'xml/xml.js',
-  'yaml/yaml.js',
-]