Merge "Normalize selection ranges for copy"
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index 13e5ca6..52657f3 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -1475,6 +1475,7 @@
     "show_whitespace_errors": true,
     "hide_line_numbers": true,
     "match_brackets": true,
+    "line_wrapping": false,
     "auto_close_brackets": true
   }
 ----
@@ -1508,6 +1509,7 @@
     "syntax_highlighting": true,
     "hide_line_numbers": true,
     "match_brackets": true,
+    "line_wrapping": false,
     "auto_close_brackets": true
   }
 ----
@@ -2212,6 +2214,8 @@
 file was added; the right pane is empty when a file was deleted.
 |`match_brackets`              |not set if `false`|
 Whether matching brackets should be highlighted.
+|`line_wrapping`               |not set if `false`|
+Whether to enable line wrapping or not.
 |===========================================
 
 [[diff-preferences-input]]
@@ -2265,6 +2269,8 @@
 True if the line numbers should be hidden.
 |`tab_size`                    |optional|
 Number of spaces that should be used to display one tab.
+|`line_wrapping`               |optional|
+Whether to enable line wrapping or not.
 |===========================================
 
 [[edit-preferences-info]]
@@ -2302,6 +2308,8 @@
 Whether line numbers should be hidden.
 |`match_brackets`              |not set if `false`|
 Whether matching brackets should be highlighted.
+|`line_wrapping`               |not set if `false`|
+Whether to enable line wrapping or not.
 |`auto_close_brackets`         |not set if `false`|
 Whether brackets and quotes should be auto-closed during typing.
 |===========================================
diff --git a/Documentation/user-review-ui.txt b/Documentation/user-review-ui.txt
index b7311d6..838a433 100644
--- a/Documentation/user-review-ui.txt
+++ b/Documentation/user-review-ui.txt
@@ -1108,6 +1108,15 @@
 +
 Large files that exceed 4000 lines will not be fully rendered.
 
+- [[line-wrapping]]`Line Wrapping`:
++
+Controls weather to enable line wrapping or not.
++
+If `false` is selected then line wrapping is disabled.
+This is the default option.
++
+If `true` is selected then line wrapping is enabled.
+
 [[keyboard-shortcuts]]
 == Keyboard Shortcuts
 
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/DiffPreferencesIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/DiffPreferencesIT.java
index a4707dc..9236176 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/DiffPreferencesIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/DiffPreferencesIT.java
@@ -99,6 +99,7 @@
     i.renderEntireFile ^= true;
     i.hideEmptyPane ^= true;
     i.matchBrackets ^= true;
+    i.lineWrapping ^= true;
 
     DiffPreferencesInfo o = gApi.accounts()
         .id(admin.getId().toString())
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/EditPreferencesIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/EditPreferencesIT.java
index dd5bcbb..9eb6918 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/EditPreferencesIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/EditPreferencesIT.java
@@ -42,6 +42,7 @@
     assertThat(out.syntaxHighlighting).isTrue();
     assertThat(out.hideLineNumbers).isNull();
     assertThat(out.matchBrackets).isTrue();
+    assertThat(out.lineWrapping).isNull();
     assertThat(out.autoCloseBrackets).isNull();
     assertThat(out.showBase).isNull();
     assertThat(out.theme).isEqualTo(Theme.DEFAULT);
@@ -58,6 +59,7 @@
     out.syntaxHighlighting = false;
     out.hideLineNumbers = true;
     out.matchBrackets = false;
+    out.lineWrapping = true;
     out.autoCloseBrackets = true;
     out.showBase = true;
     out.theme = Theme.TWILIGHT;
@@ -93,6 +95,7 @@
     assertThat(out.syntaxHighlighting).isNull();
     assertThat(out.hideLineNumbers).isEqualTo(in.hideLineNumbers);
     assertThat(out.matchBrackets).isNull();
+    assertThat(out.lineWrapping).isEqualTo(in.lineWrapping);
     assertThat(out.autoCloseBrackets).isEqualTo(in.autoCloseBrackets);
     assertThat(out.showBase).isEqualTo(in.showBase);
     assertThat(out.theme).isEqualTo(in.theme);
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java
index 1373b09..d246996 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java
@@ -56,6 +56,7 @@
   public Boolean renderEntireFile;
   public Boolean hideEmptyPane;
   public Boolean matchBrackets;
+  public Boolean lineWrapping;
   public Theme theme;
   public Whitespace ignoreWhitespace;
   public Boolean retainHeader;
@@ -88,6 +89,7 @@
     i.renderEntireFile = false;
     i.hideEmptyPane = false;
     i.matchBrackets = false;
+    i.lineWrapping = false;
     return i;
   }
 }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java
index 1f7b84a..84c61b7 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java
@@ -26,6 +26,7 @@
   public Boolean syntaxHighlighting;
   public Boolean hideLineNumbers;
   public Boolean matchBrackets;
+  public Boolean lineWrapping;
   public Boolean autoCloseBrackets;
   public Boolean showBase;
   public Theme theme;
@@ -43,6 +44,7 @@
     i.syntaxHighlighting = true;
     i.hideLineNumbers = false;
     i.matchBrackets = true;
+    i.lineWrapping = false;
     i.autoCloseBrackets = false;
     i.showBase = false;
     i.theme = Theme.DEFAULT;
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/WebLinkInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/WebLinkInfo.java
index d9a34bf..4dd8f02 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/WebLinkInfo.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/WebLinkInfo.java
@@ -14,6 +14,8 @@
 
 package com.google.gerrit.extensions.common;
 
+import com.google.gerrit.extensions.webui.WebLink.Target;
+
 public class WebLinkInfo {
   public String name;
   public String imageUrl;
@@ -26,4 +28,8 @@
     this.url = url;
     this.target = target;
   }
+
+  public WebLinkInfo(String name, String imageUrl, String url) {
+    this(name, imageUrl, url, Target.SELF);
+  }
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
index 539d53b..2b4c821 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
@@ -479,14 +479,14 @@
       if (preferUnified()) {
         unified(token, baseId, id, side, line);
       } else {
-        codemirror(token, baseId, id, side, line, false);
+        codemirror(token, baseId, id, side, line);
       }
     } else if ("sidebyside".equals(panel)) {
-      codemirror(token, baseId, id, side, line, false);
+      codemirror(token, baseId, id, side, line);
     } else if ("unified".equals(panel)) {
       unified(token, baseId, id, side, line);
     } else if ("edit".equals(panel)) {
-      codemirror(token, null, id, side, line, true);
+      codemirrorForEdit(token, id, line);
     } else {
       Gerrit.display(token, new NotFoundScreen());
     }
@@ -509,14 +509,22 @@
   }
 
   private static void codemirror(final String token, final PatchSet.Id baseId,
-      final Patch.Key id, final DisplaySide side, final int line,
-      final boolean edit) {
+      final Patch.Key id, final DisplaySide side, final int line) {
     GWT.runAsync(new AsyncSplit(token) {
       @Override
       public void onSuccess() {
-        Gerrit.display(token, edit
-            ? new EditScreen(baseId, id, line)
-            : new SideBySide(baseId, id.getParentKey(), id.get(), side, line));
+        Gerrit.display(token,
+            new SideBySide(baseId, id.getParentKey(), id.get(), side, line));
+      }
+    });
+  }
+
+  private static void codemirrorForEdit(final String token, final Patch.Key id,
+      final int line) {
+    GWT.runAsync(new AsyncSplit(token) {
+      @Override
+      public void onSuccess() {
+        Gerrit.display(token, new EditScreen(id, line));
       }
     });
   }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/DiffPreferences.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/DiffPreferences.java
index a3fc6dd..7c707b2 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/DiffPreferences.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/DiffPreferences.java
@@ -48,6 +48,7 @@
     p.skipUncommented(in.skipUncommented);
     p.skipDeleted(in.skipDeleted);
     p.matchBrackets(in.matchBrackets);
+    p.lineWrapping(in.lineWrapping);
     return p;
   }
 
@@ -73,6 +74,7 @@
     p.renderEntireFile = renderEntireFile();
     p.hideEmptyPane = hideEmptyPane();
     p.matchBrackets = matchBrackets();
+    p.lineWrapping = lineWrapping();
     p.theme = theme();
     p.ignoreWhitespace = ignoreWhitespace();
   }
@@ -144,6 +146,7 @@
   public final native void skipUncommented(boolean s) /*-{ this.skip_uncommented = s }-*/;
   public final native void skipDeleted(boolean s) /*-{ this.skip_deleted = s }-*/;
   public final native void matchBrackets(boolean m) /*-{ this.match_brackets = m }-*/;
+  public final native void lineWrapping(boolean w) /*-{ this.line_wrapping = w }-*/;
   public final native boolean intralineDifference() /*-{ return this.intraline_difference || false }-*/;
   public final native boolean showLineEndings() /*-{ return this.show_line_endings || false }-*/;
   public final native boolean showTabs() /*-{ return this.show_tabs || false }-*/;
@@ -161,6 +164,7 @@
   public final native boolean skipUncommented() /*-{ return this.skip_uncommented || false }-*/;
   public final native boolean skipDeleted() /*-{ return this.skip_deleted || false }-*/;
   public final native boolean matchBrackets() /*-{ return this.match_brackets || false }-*/;
+  public final native boolean lineWrapping() /*-{ return this.line_wrapping || false }-*/;
 
   private native void setThemeRaw(String i) /*-{ this.theme = i }-*/;
   private native void setIgnoreWhitespaceRaw(String i) /*-{ this.ignore_whitespace = i }-*/;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/EditPreferences.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/EditPreferences.java
index c710fe1..ae89607 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/EditPreferences.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/EditPreferences.java
@@ -32,6 +32,7 @@
     p.syntaxHighlighting(in.syntaxHighlighting);
     p.hideLineNumbers(in.hideLineNumbers);
     p.matchBrackets(in.matchBrackets);
+    p.lineWrapping(in.lineWrapping);
     p.autoCloseBrackets(in.autoCloseBrackets);
     p.showBase(in.showBase);
     p.theme(in.theme);
@@ -50,6 +51,7 @@
     p.syntaxHighlighting = syntaxHighlighting();
     p.hideLineNumbers = hideLineNumbers();
     p.matchBrackets = matchBrackets();
+    p.lineWrapping = lineWrapping();
     p.autoCloseBrackets = autoCloseBrackets();
     p.showBase = showBase();
     p.theme = theme();
@@ -77,6 +79,7 @@
   public final native void syntaxHighlighting(boolean s) /*-{ this.syntax_highlighting = s }-*/;
   public final native void hideLineNumbers(boolean s) /*-{ this.hide_line_numbers = s }-*/;
   public final native void matchBrackets(boolean m) /*-{ this.match_brackets = m }-*/;
+  public final native void lineWrapping(boolean w) /*-{ this.line_wrapping = w }-*/;
   public final native void autoCloseBrackets(boolean c) /*-{ this.auto_close_brackets = c }-*/;
   public final native void showBase(boolean s) /*-{ this.show_base = s }-*/;
 
@@ -114,6 +117,7 @@
   public final native boolean syntaxHighlighting() /*-{ return this.syntax_highlighting || false }-*/;
   public final native boolean hideLineNumbers() /*-{ return this.hide_line_numbers || false }-*/;
   public final native boolean matchBrackets() /*-{ return this.match_brackets || false }-*/;
+  public final native boolean lineWrapping() /*-{ return this.line_wrapping || false }-*/;
   public final native boolean autoCloseBrackets() /*-{ return this.auto_close_brackets || false }-*/;
   public final native boolean showBase() /*-{ return this.show_base || false }-*/;
   private native int get(String n, int d) /*-{ return this.hasOwnProperty(n) ? this[n] : d }-*/;
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 cc3c004..78d01db 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
@@ -102,6 +102,7 @@
   @UiField ToggleButton expandAllComments;
   @UiField ToggleButton renderEntireFile;
   @UiField ToggleButton matchBrackets;
+  @UiField ToggleButton lineWrapping;
   @UiField ToggleButton skipDeleted;
   @UiField ToggleButton skipUnchanged;
   @UiField ToggleButton skipUncommented;
@@ -197,6 +198,7 @@
     manualReview.setValue(prefs.manualReview());
     expandAllComments.setValue(prefs.expandAllComments());
     matchBrackets.setValue(prefs.matchBrackets());
+    lineWrapping.setValue(prefs.lineWrapping());
     skipDeleted.setValue(!prefs.skipDeleted());
     skipUnchanged.setValue(!prefs.skipUnchanged());
     skipUncommented.setValue(!prefs.skipUncommented());
@@ -503,6 +505,15 @@
         prefs.matchBrackets());
   }
 
+  @UiHandler("lineWrapping")
+  void onLineWrapping(ValueChangeEvent<Boolean> e) {
+    prefs.lineWrapping(e.getValue());
+    view.getCmFromSide(DisplaySide.A).setOption("lineWrapping",
+        prefs.lineWrapping());
+    view.getCmFromSide(DisplaySide.B).setOption("lineWrapping",
+        prefs.lineWrapping());
+  }
+
   @UiHandler("skipDeleted")
   void onSkipDeleted(ValueChangeEvent<Boolean> e) {
     prefs.skipDeleted(!e.getValue());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.ui.xml
index 78e113a..4465d63 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.ui.xml
@@ -298,6 +298,13 @@
         </g:ToggleButton></td>
       </tr>
       <tr>
+        <th><ui:msg>Line Wrapping</ui:msg></th>
+        <td><g:ToggleButton ui:field='lineWrapping'>
+          <g:upFace><ui:msg>Off</ui:msg></g:upFace>
+          <g:downFace><ui:msg>On</ui:msg></g:downFace>
+        </g:ToggleButton></td>
+      </tr>
+      <tr>
         <th><ui:msg>Skip Deleted Files</ui:msg></th>
         <td><g:ToggleButton ui:field='skipDeleted'>
           <g:upFace><ui:msg>Yes</ui:msg></g:upFace>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java
index 750cd6b..dbe7e5d 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java
@@ -251,8 +251,8 @@
       .set("inputStyle", "textarea")
       .set("keyMap", "vim_ro")
       .set("lineNumbers", prefs.showLineNumbers())
-      .set("lineWrapping", false)
       .set("matchBrackets", prefs.matchBrackets())
+      .set("lineWrapping", prefs.lineWrapping())
       .set("mode", getFileSize() == FileSize.SMALL ? getContentType(meta) : null)
       .set("readOnly", true)
       .set("scrollbarStyle", "overlay")
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideTable.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideTable.ui.xml
index 1eaf67f..34b64a1 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideTable.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideTable.ui.xml
@@ -78,7 +78,9 @@
     .b { border-left: 1px solid #ddd; }
 
     .a .diff { background-color: #faa; }
-    .b .diff { background-color: #9f9; }
+    /* Set min-width for lineWrapping to make sure it gets enough width
+       before lineWrapping and to make sure it dosent do a ugly line wrap */
+    .b .diff { background-color: #9f9; min-width: 60em; }
     .a .intralineBg { background-color: #fee; }
     .b .intralineBg { background-color: #dfd; }
     .noIntraline .a .intralineBg { background-color: #faa; }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.java
index 0a27ea1..6694e7c 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.java
@@ -68,6 +68,7 @@
   @UiField ToggleButton whitespaceErrors;
   @UiField ToggleButton lineNumbers;
   @UiField ToggleButton matchBrackets;
+  @UiField ToggleButton lineWrapping;
   @UiField ToggleButton autoCloseBrackets;
   @UiField ToggleButton showBase;
   @UiField ListBox theme;
@@ -104,6 +105,7 @@
     whitespaceErrors.setValue(prefs.showWhitespaceErrors());
     lineNumbers.setValue(prefs.hideLineNumbers());
     matchBrackets.setValue(prefs.matchBrackets());
+    lineWrapping.setValue(prefs.lineWrapping());
     autoCloseBrackets.setValue(prefs.autoCloseBrackets());
     showBase.setValue(prefs.showBase());
     setTheme(prefs.theme());
@@ -205,6 +207,14 @@
     }
   }
 
+  @UiHandler("lineWrapping")
+  void onLineWrapping(ValueChangeEvent<Boolean> e) {
+    prefs.lineWrapping(e.getValue());
+    if (view != null) {
+      view.getEditor().setOption("lineWrapping", prefs.lineWrapping());
+    }
+  }
+
   @UiHandler("autoCloseBrackets")
   void onCloseBrackets(ValueChangeEvent<Boolean> e) {
     prefs.autoCloseBrackets(e.getValue());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.ui.xml
index 5fa3f16..6379b67 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.ui.xml
@@ -239,6 +239,13 @@
         </g:ToggleButton></td>
       </tr>
       <tr>
+        <th><ui:msg>Line Wrapping</ui:msg></th>
+        <td><g:ToggleButton ui:field='lineWrapping'>
+          <g:upFace><ui:msg>Off</ui:msg></g:upFace>
+          <g:downFace><ui:msg>On</ui:msg></g:downFace>
+        </g:ToggleButton></td>
+      </tr>
+      <tr>
         <th><ui:msg>Auto Close Brackets</ui:msg></th>
         <td><g:ToggleButton ui:field='autoCloseBrackets'>
           <g:upFace><ui:msg>Off</ui:msg></g:upFace>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java
index da7ca44..490e028 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java
@@ -99,7 +99,6 @@
     String hideBase();
   }
 
-  private final PatchSet.Id base;
   private final PatchSet.Id revision;
   private final String path;
   private final int startLine;
@@ -130,8 +129,7 @@
   private HandlerRegistration closeHandler;
   private int generation;
 
-  public EditScreen(PatchSet.Id base, Patch.Key patch, int startLine) {
-    this.base = base;
+  public EditScreen(Patch.Key patch, int startLine) {
     this.revision = patch.getParentKey();
     this.path = patch.get();
     this.startLine = startLine - 1;
@@ -232,7 +230,6 @@
       // TODO(davido): We probably want to create dedicated GET EditScreenMeta
       // REST endpoint. Abuse GET diff for now, as it retrieves links we need.
       DiffApi.diff(revision, path)
-        .base(base)
         .webLinksOnly()
         .get(group1.addFinal(new AsyncCallback<DiffInfo>() {
           @Override
@@ -614,7 +611,7 @@
     sbs.setHTML(new ImageResourceRenderer()
         .render(Gerrit.RESOURCES.sideBySideDiff()));
     sbs.setTargetHistoryToken(
-        Dispatcher.toPatch("sidebyside", base, new Patch.Key(revision, path)));
+        Dispatcher.toPatch("sidebyside", null, new Patch.Key(revision, path)));
     sbs.setTitle(PatchUtil.C.sideBySideDiff());
     linkPanel.add(sbs);
 
@@ -622,7 +619,7 @@
     unified.setHTML(new ImageResourceRenderer()
         .render(Gerrit.RESOURCES.unifiedDiff()));
     unified.setTargetHistoryToken(
-        Dispatcher.toPatch("unified", base, new Patch.Key(revision, path)));
+        Dispatcher.toPatch("unified", null, new Patch.Key(revision, path)));
     unified.setTitle(PatchUtil.C.unifiedDiff());
     linkPanel.add(unified);
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
index 5d0f4f1..384a9ae 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
@@ -201,7 +201,7 @@
         initPatchSet();
       } catch (OrmException e) {
         return ruleError("Error looking up patch set "
-            + control.getChange().currentPatchSetId());
+            + control.getChange().currentPatchSetId(), e);
       }
       if (patchSet.isDraft()) {
         return cannotSubmitDraft();
@@ -372,7 +372,7 @@
       initPatchSet();
     } catch (OrmException e) {
       return typeError("Error looking up patch set "
-          + control.getChange().currentPatchSetId());
+          + control.getChange().currentPatchSetId(), e);
     }
 
     try {
diff --git a/lib/BUCK b/lib/BUCK
index d063c12..6e843c4 100644
--- a/lib/BUCK
+++ b/lib/BUCK
@@ -13,6 +13,7 @@
 define_license(name = 'codemirror-minified')
 define_license(name = 'codemirror-original')
 define_license(name = 'diffy')
+define_license(name = 'es6-promise')
 define_license(name = 'fetch')
 define_license(name = 'h2')
 define_license(name = 'highlightjs')
diff --git a/lib/LICENSE-es6-promise b/lib/LICENSE-es6-promise
new file mode 100644
index 0000000..954ec59
--- /dev/null
+++ b/lib/LICENSE-es6-promise
@@ -0,0 +1,19 @@
+Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/lib/js/BUCK b/lib/js/BUCK
index 71b1474..1c46d35 100644
--- a/lib/js/BUCK
+++ b/lib/js/BUCK
@@ -100,6 +100,14 @@
 )
 
 bower_component(
+  name = 'es6-promise',
+  package = 'stefanpenner/es6-promise',
+  version = '3.3.0',
+  license = 'es6-promise',
+  sha1 = 'a3a797bb22132f1ef75f9a2556173f81870c2e53',
+)
+
+bower_component(
   name = 'fetch',
   package = 'fetch',
   version = '1.0.0',
diff --git a/polygerrit-ui/BUCK b/polygerrit-ui/BUCK
index e26e40c..80f9f29 100644
--- a/polygerrit-ui/BUCK
+++ b/polygerrit-ui/BUCK
@@ -3,6 +3,7 @@
 bower_components(
   name = 'polygerrit_components',
   deps = [
+    '//lib/js:es6-promise',
     '//lib/js:fetch',
     '//lib/js:highlightjs',
     '//lib/js:iron-autogrow-textarea',
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html b/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
index ff8610b..643d55d 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
@@ -37,6 +37,7 @@
 
     setup(function() {
       element = fixture('basic');
+      sinon.stub(element.$.restAPI, 'getLoggedIn').returns(true);
     });
 
     test('reply event', function(done) {
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.html b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.html
index 2b05d99..957b2ce 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.html
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.html
@@ -53,7 +53,8 @@
           on-commit="_handleInputCommit"
           allowNonSuggestedValues
           multi
-          borderless></gr-autocomplete>
+          borderless
+          tab-complete-without-commit></gr-autocomplete>
       <gr-button id="searchButton">Search</gr-button>
       <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
     </form>
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
index d8121b7..c33f889 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
@@ -264,15 +264,22 @@
 
       return this._fetchSuggestions(trimmedInput)
           .then(function(operators) {
-            if (!operators) { return []; }
+            if (!operators || !operators.length) { return []; }
             return operators
-                // Disallow autocomplete values that exactly match the str.
-                .filter(function(operator) {
-                  return input.indexOf(operator.toLowerCase()) == -1;
-                })
                 // Prioritize results that start with the input.
-                .sort(function(operator) {
-                  return operator.indexOf(trimmedInput);
+                .sort(function(a, b) {
+                  var aContains = a.toLowerCase().indexOf(trimmedInput);
+                  var bContains = b.toLowerCase().indexOf(trimmedInput);
+                  if (aContains === bContains) {
+                    return a.localeCompare(b);
+                  }
+                  if (aContains === -1) {
+                    return 1;
+                  }
+                  if (bContains === -1) {
+                    return -1;
+                  }
+                  return aContains - bContains;
                 })
                 // Return only the first {MAX_AUTOCOMPLETE_RESULTS} results.
                 .slice(0, MAX_AUTOCOMPLETE_RESULTS - 1)
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.html b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.html
index a6f1817..696efcd 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.html
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.html
@@ -112,6 +112,8 @@
         sinon.stub(element.$.restAPI, 'getSuggestedGroups', function() {
           return Promise.resolve({
             Polygerrit: 0,
+            gerrit: 0,
+            gerrittest: 0,
           });
         });
         sinon.stub(element.$.restAPI, 'getSuggestedProjects', function() {
@@ -174,6 +176,15 @@
               done();
             });
       });
+
+      test('Autocomplete doesnt override exact matches to input',
+          function(done) {
+        return element._getSearchSuggestions('ownerin:gerrit')
+            .then(function(suggestions) {
+              assert.equal(suggestions[0].value, 'ownerin:gerrit');
+              done();
+            });
+      });
     });
   });
 </script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js
index 6844416..c524f7f 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js
@@ -163,8 +163,8 @@
   };
 
   /**
-   * Re-renders the DIV.contentText alement for the given side and range of diff
-   * content.
+   * Re-renders the DIV.contentText elements for the given side and range of
+   * diff content.
    */
   GrDiffBuilder.prototype._renderContentByRange = function(start, end, side) {
     var lines = [];
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
index 60c7f25..4f3cd34 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
@@ -78,6 +78,15 @@
         value: false,
       },
 
+      /**
+       * When true, tab key autocompletes but does not fire the commit event.
+       * See Issue 4556.
+       */
+      tabCompleteWithoutCommit: {
+        type: Boolean,
+        value: false,
+      },
+
       value: Object,
 
       /**
@@ -185,7 +194,7 @@
         case 9: // Tab
           if (this._suggestions.length > 0) {
             e.preventDefault();
-            this._commit();
+            this._commit(this.tabCompleteWithoutCommit);
             this._suggestions = [];
           }
           break;
@@ -231,7 +240,14 @@
       this._commit();
     },
 
-    _commit: function() {
+    /**
+     * Commits the suggestion, optionally firing the commit event.
+     *
+     * @param {Boolean} silent Allows for silent committing of an autocomplete
+     *     suggestion in order to handle cases like tab-to-complete without
+     *     firing the commit event.
+     */
+    _commit: function(silent) {
       // Allow values that are not in suggestion list iff suggestions are empty.
       if (this._suggestions.length > 0) {
         this._updateValue(this._suggestions, this._index);
@@ -252,7 +268,9 @@
         }
       }
 
-      this.fire('commit', {value: value});
+      if (!silent) {
+        this.fire('commit', {value: value});
+      }
     },
   });
 })();
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html
index ccc578c..ba1cbd2 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html
@@ -252,5 +252,18 @@
       assert.isTrue(commitStub.called);
       commitStub.restore();
     });
+
+    test('tabCompleteWithoutCommit flag functions', function() {
+      var commitHandler = sinon.spy();
+      element.addEventListener('commit', commitHandler);
+      element._suggestions = ['tunnel snakes rule!'];
+      element.tabCompleteWithoutCommit = true;
+      MockInteractions.pressAndReleaseKeyOn(element.$.input, 9); // tab
+      assert.isFalse(commitHandler.called);
+      element.tabCompleteWithoutCommit = false;
+      element._suggestions = ['tunnel snakes rule!'];
+      MockInteractions.pressAndReleaseKeyOn(element.$.input, 9); // tab
+      assert.isTrue(commitHandler.called);
+    });
   });
 </script>
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html
index 72ae4e4..4980cba 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html
@@ -15,6 +15,7 @@
 -->
 
 <link rel="import" href="../../../bower_components/polymer/polymer.html">
+<script src="../../../bower_components/es6-promise/dist/es6-promise.min.js"></script>
 <script src="../../../bower_components/fetch/fetch.js"></script>
 
 <dom-module id="gr-rest-api-interface">
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index 632ba06..d22622a 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -94,7 +94,6 @@
     fetchJSON: function(url, opt_errFn, opt_cancelCondition, opt_params,
         opt_opts) {
       opt_opts = opt_opts || {};
-
       var fetchOptions = {
         credentials: 'same-origin',
         headers: opt_opts.headers,
@@ -312,6 +311,10 @@
           ListChangesOption.LABELS,
           ListChangesOption.DETAILED_ACCOUNTS
       );
+      // Issue 4524: respect legacy token with max sortkey.
+      if (opt_offset === 'n,z') {
+        opt_offset = 0;
+      }
       var params = {
         n: changesPerPage,
         O: options,
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
index 8f994f0..c340ee3 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
@@ -324,5 +324,11 @@
         });
       });
     });
+
+    test('legacy n,z key in change url is replaced', function() {
+      var stub = sandbox.stub(element, 'fetchJSON');
+      element.getChanges(1, null, 'n,z');
+      assert.equal(stub.args[0][3].S, 0);
+    });
   });
 </script>