Merge "Fix diff mode selector initial state"
diff --git a/WORKSPACE b/WORKSPACE
index c8b46de..ac81208 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -24,24 +24,30 @@
   sha1 = '83cd2cd674a217ade95a4bb83a8a14f351f48bd0',
 )
 
-GUICE_VERS = '4.0'
+GUICE_VERS = '4.1.0'
 
 maven_jar(
   name = 'guice_library',
   artifact = 'com.google.inject:guice:' + GUICE_VERS,
-  sha1 = '0f990a43d3725781b6db7cd0acf0a8b62dfd1649',
+  sha1 = 'eeb69005da379a10071aa4948c48d89250febb07',
 )
 
 maven_jar(
   name = 'guice_assistedinject',
   artifact = 'com.google.inject.extensions:guice-assistedinject:' + GUICE_VERS,
-  sha1 = '8fa6431da1a2187817e3e52e967535899e2e46ca',
+  sha1 = 'af799dd7e23e6fe8c988da12314582072b07edcb',
 )
 
 maven_jar(
   name = 'guice_servlet',
   artifact = 'com.google.inject.extensions:guice-servlet:' + GUICE_VERS,
-  sha1 = '4503da866f4c402b5090579b40c1c4aaefabb164',
+  sha1 = '90ac2db772d9b85e2b05417b74f7464bcc061dcb',
+)
+
+maven_jar(
+  name = 'multibindings',
+  artifact = 'com.google.inject.extensions:guice-multibindings:' + GUICE_VERS,
+  sha1 = '3b27257997ac51b0f8d19676f1ea170427e86d51',
 )
 
 maven_jar(
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java b/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java
index 943be7e..3d99883 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java
+++ b/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java
@@ -82,7 +82,6 @@
       Modes.I.htmlmixed(),
       Modes.I.http(),
       Modes.I.idl(),
-      Modes.I.jade(),
       Modes.I.javascript(),
       Modes.I.jinja2(),
       Modes.I.jsx(),
@@ -110,6 +109,7 @@
       Modes.I.powershell(),
       Modes.I.properties(),
       Modes.I.protobuf(),
+      Modes.I.pug(),
       Modes.I.puppet(),
       Modes.I.python(),
       Modes.I.q(),
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 668a57f..218b96c 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/mode/Modes.java
+++ b/gerrit-gwtui/src/main/java/net/codemirror/mode/Modes.java
@@ -67,7 +67,6 @@
   @Source("htmlmixed.js") @DoNotEmbed DataResource htmlmixed();
   @Source("http.js") @DoNotEmbed DataResource http();
   @Source("idl.js") @DoNotEmbed DataResource idl();
-  @Source("jade.js") @DoNotEmbed DataResource jade();
   @Source("javascript.js") @DoNotEmbed DataResource javascript();
   @Source("jinja2.js") @DoNotEmbed DataResource jinja2();
   @Source("jsx.js") @DoNotEmbed DataResource jsx();
@@ -95,6 +94,7 @@
   @Source("powershell.js") @DoNotEmbed DataResource powershell();
   @Source("properties.js") @DoNotEmbed DataResource properties();
   @Source("protobuf.js") @DoNotEmbed DataResource protobuf();
+  @Source("pug.js") @DoNotEmbed DataResource pug();
   @Source("puppet.js") @DoNotEmbed DataResource puppet();
   @Source("python.js") @DoNotEmbed DataResource python();
   @Source("q.js") @DoNotEmbed DataResource q();
diff --git a/gerrit-plugin-api/BUILD b/gerrit-plugin-api/BUILD
index 2c18ca6..c761703 100644
--- a/gerrit-plugin-api/BUILD
+++ b/gerrit-plugin-api/BUILD
@@ -28,24 +28,33 @@
     '//gerrit-extension-api:api',
     '//gerrit-gwtexpui:server',
     '//gerrit-reviewdb:server',
-    '//lib:args4j',
-    '//lib:blame-cache',
-    '//lib/dropwizard:dropwizard-core',
-    '//lib:guava',
-    '//lib:gwtorm',
-    '//lib:jsch',
-    '//lib:mime-util',
-    '//lib:servlet-api-3_1',
-    '//lib:velocity',
     '//lib/commons:lang',
+    '//lib/dropwizard:dropwizard-core',
     '//lib/guice:guice',
     '//lib/guice:guice-assistedinject',
     '//lib/guice:guice-servlet',
-    '//lib/jgit/org.eclipse.jgit:jgit',
+    '//lib/guice:javax-inject',
+    '//lib/guice:multibindings',
     '//lib/jgit/org.eclipse.jgit.http.server:jgit-servlet',
+    '//lib/jgit/org.eclipse.jgit:jgit',
     '//lib/joda:joda-time',
     '//lib/log:api',
     '//lib/mina:sshd',
+    '//lib/ow2:ow2-asm',
+    '//lib/ow2:ow2-asm-analysis',
+    '//lib/ow2:ow2-asm-commons',
+    '//lib/ow2:ow2-asm-util',
+    '//lib:args4j',
+    '//lib:blame-cache',
+    '//lib:guava',
+    '//lib:gwtorm',
+    '//lib:icu4j',
+    '//lib:jsch',
+    '//lib:mime-util',
+    '//lib:protobuf',
+    '//lib:servlet-api-3_1',
+    '//lib:soy',
+    '//lib:velocity',
   ],
   visibility = ['//visibility:public'],
 )
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mime/mime-types.properties b/gerrit-server/src/main/resources/com/google/gerrit/server/mime/mime-types.properties
index d51547c..5a937b6 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mime/mime-types.properties
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mime/mime-types.properties
@@ -97,7 +97,7 @@
 in = text/x-properties
 ini = text/x-properties
 intr = text/x-dylan
-jade = text/x-jade
+jade = text/x-pug
 java = text/x-java
 jl = text/x-julia
 jruby = text/x-ruby
@@ -163,6 +163,7 @@
 ps1 = application/x-powershell
 psd1 = application/x-powershell
 psm1 = application/x-powershell
+pug = text/x-pug
 py = text/x-python
 pyw = text/x-python
 pyx = text/x-cython
diff --git a/lib/BUILD b/lib/BUILD
index 5872328..a490038 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -151,11 +151,6 @@
   visibility = ['//visibility:public'],
 )
 
-java_library(
-  name = 'soy',
-  exports = ['@soy//jar'],
-  visibility = ['//visibility:public'],
-)
 
 java_library(
   name = 'h2',
@@ -208,3 +203,31 @@
   exports = ['@derby//jar'],
   visibility = ['//visibility:public'],
 )
+
+java_library(
+  name = 'soy',
+  exports = ['@soy//jar'],
+  runtime_deps = [
+    ':args4j',
+    ':guava',
+    ':gson',
+    ':icu4j',
+    ':jsr305',
+    ':protobuf',
+    '//lib/guice:guice',
+    '//lib/guice:guice-assistedinject',
+    '//lib/guice:multibindings',
+    '//lib/guice:javax-inject',
+    '//lib/ow2:ow2-asm',
+    '//lib/ow2:ow2-asm-analysis',
+    '//lib/ow2:ow2-asm-commons',
+    '//lib/ow2:ow2-asm-util',
+  ],
+  visibility = ['//visibility:public'],
+)
+
+java_library(
+  name = 'icu4j',
+  exports = [ '@icu4j//jar' ],
+  visibility = ['//visibility:public'],
+)
diff --git a/lib/codemirror/BUCK b/lib/codemirror/BUCK
index a0e0e9a..56145ea 100644
--- a/lib/codemirror/BUCK
+++ b/lib/codemirror/BUCK
@@ -1,14 +1,14 @@
 include_defs('//lib/maven.defs')
 include_defs('//lib/codemirror/cm.defs')
 
-VERSION = '5.17.0'
+VERSION = '5.18.2'
 TOP = 'META-INF/resources/webjars/codemirror/%s' % VERSION
 TOP_MINIFIED = 'META-INF/resources/webjars/codemirror-minified/%s' % VERSION
 
 maven_jar(
   name = 'codemirror-minified',
   id = 'org.webjars.npm:codemirror-minified:' + VERSION,
-  sha1 = '05ad901fc9be67eb7ba8997d896488093deb898e',
+  sha1 = '6755af157a7eaf2401468906bef67bbacc3c97f6',
   attach_source = False,
   license = 'codemirror-minified',
   visibility = [],
@@ -17,7 +17,7 @@
 maven_jar(
   name = 'codemirror-original',
   id = 'org.webjars.npm:codemirror:' + VERSION,
-  sha1 = 'c025b8d9aca1061e26d1fa482bea0ecea1412e85',
+  sha1 = '18c721ae88eed27cddb458c42f5d221fa3d9713e',
   attach_source = False,
   license = 'codemirror-original',
   visibility = [],
diff --git a/lib/codemirror/cm.defs b/lib/codemirror/cm.defs
index baf2ce5..a1be90f 100644
--- a/lib/codemirror/cm.defs
+++ b/lib/codemirror/cm.defs
@@ -132,7 +132,6 @@
   'htmlmixed',
   'http',
   'idl',
-  'jade',
   'javascript',
   'jinja2',
   'jsx',
@@ -160,6 +159,7 @@
   'powershell',
   'properties',
   'protobuf',
+  'pug',
   'puppet',
   'python',
   'q',
diff --git a/lib/guice/BUILD b/lib/guice/BUILD
index acade50..5850af2 100644
--- a/lib/guice/BUILD
+++ b/lib/guice/BUILD
@@ -3,6 +3,7 @@
   exports = [
     ':guice_library',
     ':javax-inject',
+    ':multibindings',
   ],
   visibility = ['//visibility:public'],
 )
@@ -36,4 +37,11 @@
 java_library(
   name = 'javax-inject',
   exports = ['@javax_inject//jar'],
+  visibility = ['//visibility:public'],
+)
+
+java_library(
+  name = 'multibindings',
+  exports = [ '@multibindings//jar' ],
+  visibility = ['//visibility:public'],
 )
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
index 9865a3f..61b10e8 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
@@ -137,6 +137,12 @@
         border: 1px solid #ddd;
         margin: 1em var(--default-horizontal-margin);
       }
+      .patchInfo--oldPatchSet .patchInfo-header {
+        background-color: #fff9c4;
+      }
+      .patchInfo--oldPatchSet .latestPatchContainer {
+        display: initial;
+      }
       .patchInfo-header,
       gr-file-list {
         padding: .5em calc(var(--default-horizontal-margin) / 2);
@@ -147,6 +153,9 @@
         display: flex;
         justify-content: space-between;
       }
+      .latestPatchContainer {
+        display: none;
+      }
       @media screen and (max-width: 50em) {
         .header {
           align-items: flex-start;
@@ -254,12 +263,12 @@
           </div>
         </div>
       </section>
-      <section class="patchInfo">
+      <section class$="patchInfo [[_computePatchInfoClass(_patchRange.patchNum, _allPatchSets)]]">
         <div class="patchInfo-header">
           <div>
             <label class="patchSelectLabel" for="patchSetSelect">Patch set</label>
             <select id="patchSetSelect" on-change="_handlePatchChange">
-              <template is="dom-repeat" items="{{_allPatchSets}}" as="patchNumber">
+              <template is="dom-repeat" items="[[_allPatchSets]]" as="patchNumber">
                 <option value$="[[patchNumber]]" selected$="[[_computePatchIndexIsSelected(index, _patchRange.patchNum)]]">
                   <span>[[patchNumber]]</span>
                   /
@@ -273,6 +282,10 @@
                   class="download"
                   on-tap="_handleDownloadTap">Download</gr-button>
             </span>
+            <span class="latestPatchContainer">
+              /
+              <a href$="/c/[[_change._number]]">Go to latest patch set</a>
+            </span>
           </div>
           <gr-commit-info
               change="[[_change]]"
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
index 4f965dc..b6c18e5 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
@@ -396,6 +396,14 @@
       return allPatchSets[allPatchSets.length - 1];
     },
 
+    _computePatchInfoClass: function(patchNum, allPatchSets) {
+      if (parseInt(patchNum, 10) ===
+          this._computeLatestPatchNum(allPatchSets)) {
+        return '';
+      }
+      return 'patchInfo--oldPatchSet';
+    },
+
     _computeAllPatchSets: function(change) {
       var patchNums = [];
       for (var rev in change.revisions) {
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
index b14ded7..2cdeab4 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
@@ -397,5 +397,14 @@
       assert(openSpy.lastCall.calledWithExactly(FocusTarget.CCS),
           '_openReplyDialog should have been passed CCS');
     });
+
+    test('class is applied to file list on old patch set', function() {
+      var allPatcheSets = [1, 2, 4];
+      assert.equal(element._computePatchInfoClass('1', allPatcheSets),
+          'patchInfo--oldPatchSet');
+      assert.equal(element._computePatchInfoClass('2', allPatcheSets),
+          'patchInfo--oldPatchSet');
+      assert.equal(element._computePatchInfoClass('4', allPatcheSets), '');
+    });
   });
 </script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
index 18c0602..40a90b7 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
@@ -77,6 +77,7 @@
           _builder: Object,
           _groups: Array,
           _layers: Array,
+          _showTabs: Boolean,
         },
 
         get diffElement() {
@@ -92,6 +93,7 @@
           this._layers = [
             this.$.syntaxLayer,
             this._createIntralineLayer(),
+            this._createTabIndicatorLayer(),
             this.$.rangeLayer,
           ];
 
@@ -102,6 +104,7 @@
 
         render: function(comments, prefs) {
           this.$.syntaxLayer.enabled = prefs.syntax_highlighting;
+          this._showTabs = !!prefs.show_tabs;
 
           // Stop the processor (if it's running).
           this.$.processor.cancel();
@@ -330,6 +333,31 @@
           };
         },
 
+        _createTabIndicatorLayer: function() {
+          var show = (function() { return this._showTabs; }).bind(this);
+          return {
+            addListener: function() {},
+            annotate: function(el, line) {
+              // If visible tabs are disabled, do nothing.
+              if (!show()) { return; }
+
+              // Find and annotate the locations of tabs.
+              var split = line.text.split('\t');
+              if (!split) { return; }
+              for (var i = 0, pos = 0; i < split.length - 1; i++) {
+                // Skip forward by the length of the content
+                pos += split[i].length;
+
+                GrAnnotation.annotateElement(el, pos, 1,
+                    'style-scope gr-diff tab-indicator');
+
+                // Skip forward by one tab character.
+                pos++;
+              }
+            },
+          };
+        },
+
         /**
          * In pages with large diffs, creating the first comment thread can be
          * slow because nested Polymer elements (particularly
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 2090e98..670885a 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
@@ -493,7 +493,7 @@
     for (var i = 0; i < split.length - 1; i++) {
       offset += split[i].length;
       width = tabSize - (offset % tabSize);
-      result += split[i] + this._getTabWrapper(width, this._prefs.show_tabs);
+      result += split[i] + this._getTabWrapper(width);
       offset += width;
     }
     if (split.length) {
@@ -503,7 +503,7 @@
     return result;
   };
 
-  GrDiffBuilder.prototype._getTabWrapper = function(tabSize, showTabs) {
+  GrDiffBuilder.prototype._getTabWrapper = function(tabSize) {
     // Force this to be a number to prevent arbitrary injection.
     tabSize = +tabSize;
     if (isNaN(tabSize)) {
@@ -511,9 +511,6 @@
     }
 
     var str = '<span class="style-scope gr-diff tab ';
-    if (showTabs) {
-      str += 'withIndicator';
-    }
     str += '" style="';
     // TODO(andybons): CSS tab-size is not supported in IE.
     str += 'tab-size:' + tabSize + ';';
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html
index 187a5cd..af44629 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html
@@ -414,6 +414,117 @@
       });
     });
 
+    suite('tab indicators', function() {
+      var sandbox;
+      var element;
+      var layer;
+
+      setup(function() {
+        sandbox = sinon.sandbox.create();
+        element = fixture('basic');
+        element._showTabs = true;
+        layer = element._createTabIndicatorLayer();
+      });
+
+      teardown(function() {
+        sandbox.restore();
+      });
+
+      test('does nothing with empty line', function() {
+        var line = {text: ''};
+        var el = document.createElement('div');
+        var annotateElementStub = sandbox.stub(GrAnnotation, 'annotateElement');
+
+        layer.annotate(el, line);
+
+        assert.isFalse(annotateElementStub.called);
+      });
+
+      test('does nothing with no tabs', function() {
+        var str = 'lorem ipsum no tabs';
+        var line = {text: str};
+        var el = document.createElement('div');
+        el.textContent = str;
+        var annotateElementStub = sandbox.stub(GrAnnotation, 'annotateElement');
+
+        layer.annotate(el, line);
+
+        assert.isFalse(annotateElementStub.called);
+      });
+
+      test('annotates tab at beginning', function() {
+        var str = '\tlorem upsum';
+        var line = {text: str};
+        var el = document.createElement('div');
+        el.textContent = str;
+        var annotateElementStub = sandbox.stub(GrAnnotation, 'annotateElement');
+
+        layer.annotate(el, line);
+
+        assert.equal(annotateElementStub.callCount, 1);
+        var args = annotateElementStub.getCalls()[0].args;
+        assert.equal(args[0], el);
+        assert.equal(args[1], 0, 'offset of tab indicator');
+        assert.equal(args[2], 1, 'length of tab indicator');
+        assert.include(args[3], 'tab-indicator');
+      });
+
+      test('does not annotate when disabled', function() {
+        element._showTabs = false;
+
+        var str = '\tlorem upsum';
+        var line = {text: str};
+        var el = document.createElement('div');
+        el.textContent = str;
+        var annotateElementStub = sandbox.stub(GrAnnotation, 'annotateElement');
+
+        layer.annotate(el, line);
+
+        assert.isFalse(annotateElementStub.called);
+      });
+
+      test('annotates multiple in beginning', function() {
+        var str = '\t\tlorem upsum';
+        var line = {text: str};
+        var el = document.createElement('div');
+        el.textContent = str;
+        var annotateElementStub = sandbox.stub(GrAnnotation, 'annotateElement');
+
+        layer.annotate(el, line);
+
+        assert.equal(annotateElementStub.callCount, 2);
+
+        var args = annotateElementStub.getCalls()[0].args;
+        assert.equal(args[0], el);
+        assert.equal(args[1], 0, 'offset of tab indicator');
+        assert.equal(args[2], 1, 'length of tab indicator');
+        assert.include(args[3], 'tab-indicator');
+
+        args = annotateElementStub.getCalls()[1].args;
+        assert.equal(args[0], el);
+        assert.equal(args[1], 1, 'offset of tab indicator');
+        assert.equal(args[2], 1, 'length of tab indicator');
+        assert.include(args[3], 'tab-indicator');
+      });
+
+      test('annotates intermediate tabs', function() {
+        var str = 'lorem\tupsum';
+        var line = {text: str};
+        var el = document.createElement('div');
+        el.textContent = str;
+        var annotateElementStub = sandbox.stub(GrAnnotation, 'annotateElement');
+
+        layer.annotate(el, line);
+
+        assert.equal(annotateElementStub.callCount, 1);
+        var args = annotateElementStub.getCalls()[0].args;
+        assert.equal(args[0], el);
+        assert.equal(args[1], 5, 'offset of tab indicator');
+        assert.equal(args[2], 1, 'length of tab indicator');
+        assert.include(args[3], 'tab-indicator');
+      });
+    });
+
     suite('rendering', function() {
       var content;
       var outputEl;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
index 46612a0..2431fb0 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
@@ -152,11 +152,11 @@
       }
       .tab {
         display: inline-block;
-        position: relative;
       }
-      .tab.withIndicator {
-        color: #D68E47;
-        text-decoration: line-through;
+      .tab-indicator:before {
+        color: #C62828;
+        /* >> character */
+        content: '\00BB';
       }
     </style>
     <style include="gr-theme-default"></style>
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button.html b/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
index cc0da66..164bb2d 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
@@ -120,13 +120,14 @@
       :host([secondary]:active) {
         border-color: #941c0c;
       }
-      :host([primary][loading]),
-      :host([primary][disabled]) {
+      :host([primary][loading]) {
         background-color: #7caeff;
         border-color: transparent;
         color: #fff;
       }
-
+      :host([primary][disabled]) {
+        background-color: #888;
+      }
     </style>
     <content></content>
   </template>