[PATCH 7 of 7 V3] hgweb: expose a followlines UI in filerevision view (RFC)

Denis Laxalde denis at laxalde.org
Wed Mar 22 06:37:32 EDT 2017


# HG changeset patch
# User Denis Laxalde <denis.laxalde at logilab.fr>
# Date 1489594320 -3600
#      Wed Mar 15 17:12:00 2017 +0100
# Node ID cb742fcfe0edb2d393b59674c4eebcf8775ecfe1
# Parent  9e3ae74c1ca558f0584067c62835bec82034c245
# Available At http://hg.logilab.org/users/dlaxalde/hg
#              hg pull http://hg.logilab.org/users/dlaxalde/hg -r cb742fcfe0ed
hgweb: expose a followlines UI in filerevision view (RFC)

In filerevision view (/file/<rev>/<fname>) we add some event listeners on
mouse selection of <span> elements in the <pre class="sourcelines"> block.
Those listeners will capture the range of mouse-selected lines and, upon mouse
release, a box inviting to follow the history of selected lines will show up.
This action is advertised by a :after pseudo-element on file lines that shows
up on hover and invite to "select a block of lines to follow its history".

This is proposal implementation, comments welcome on any aspects.

diff --git a/mercurial/templates/paper/filerevision.tmpl b/mercurial/templates/paper/filerevision.tmpl
--- a/mercurial/templates/paper/filerevision.tmpl
+++ b/mercurial/templates/paper/filerevision.tmpl
@@ -73,6 +73,11 @@
 <div class="sourcefirst"> line source</div>
 <pre class="sourcelines stripes4 wrap bottomline">{text%fileline}</pre>
 </div>
+
+<script type="text/javascript"{if(nonce, ' nonce="{nonce}"')}>
+    installLineSelect("{url|urlescape}log/{symrev}/{file|urlescape}");
+</script>
+
 </div>
 </div>
 
diff --git a/mercurial/templates/static/mercurial.js b/mercurial/templates/static/mercurial.js
--- a/mercurial/templates/static/mercurial.js
+++ b/mercurial/templates/static/mercurial.js
@@ -434,6 +434,81 @@ function ajaxScrollInit(urlFormat,
     scrollHandler();
 }
 
+//** install mouse event listeners on <pre class="sourcelines"> */
+function installLineSelect(targetUri) {
+    var sourcelines = document.getElementsByClassName('sourcelines')[0];
+    if (typeof sourcelines === 'undefined') {
+        return;
+    }
+    // add event listener to the whole <pre class="sourcelines"> block to have
+    // it available in all children <span>
+    sourcelines.addEventListener('mousedown', lineSelectStart);
+
+    //** event handler for "mousedown" */
+    function lineSelectStart(e) {
+        var startElement = e.target;
+        if (startElement.tagName !== 'SPAN') {
+            // we may be on a <a>
+            return;
+        }
+        // retarget "mouseup" to sourcelines element so that if this event
+        // occurs outside the <pre> we don't miss it
+        sourcelines.setCapture();
+        // drop any prior <div id="followlines">
+        var previousInvite = document.getElementById('followlines');
+        if (previousInvite !== null) {
+            previousInvite.parentNode.removeChild(previousInvite);
+        }
+
+        var startId = parseInt(startElement.id.slice(1));
+
+        //** event handler for "mouseup" */
+        function lineSelectEnd(e) {
+            // remove "mouseup" listener
+            sourcelines.removeEventListener('mouseup', lineSelectEnd);
+
+            var endElement = e.target;
+            if (endElement.tagName !== 'SPAN') {
+                // not on <span>, probably outside <pre class="sourcelines">,
+                // we cannot compute endId
+                return;
+            }
+
+            // compute line range (startId, endId) and insert the
+            // "followlines" element
+            var endId = parseInt(endElement.id.slice(1));
+            if (endId == startId) {
+                return;
+            }
+            var inviteElement = endElement;
+            if (endId < startId) {
+                var tmp = endId;
+                endId = startId;
+                startId = tmp;
+                inviteElement = startElement;
+            }
+            var div = followlinesBox(startId, endId);
+            inviteElement.appendChild(div);
+        }
+
+        sourcelines.addEventListener('mouseup', lineSelectEnd);
+
+    }
+
+    //** return a <div id="followlines"> with a link for followlines action */
+    function followlinesBox(startId, endId) {
+        var div = document.createElement('div');
+        div.id = 'followlines';
+        var a = document.createElement('a');
+        var url = targetUri + '?patch=&linerange=' + startId + ':' + endId;
+        a.setAttribute('href', url);
+        a.textContent = 'follow lines ' + startId + ':' + endId;
+        div.appendChild(a);
+        return div;
+    }
+
+}
+
 document.addEventListener('DOMContentLoaded', function() {
    process_dates();
 }, false);
diff --git a/mercurial/templates/static/style-paper.css b/mercurial/templates/static/style-paper.css
--- a/mercurial/templates/static/style-paper.css
+++ b/mercurial/templates/static/style-paper.css
@@ -276,10 +276,28 @@ td.annotate:hover div.annotate-info { di
   float: left;
 }
 
+div.overflow pre.sourcelines > span:hover:after {
+  content: "select a block of lines to follow its history";
+  display: inline;
+  float: right;
+  line-height: 1em;
+  background-color: #FFFFFF;
+  color: #001199;
+}
+
 .sourcelines > span:target, tr:target td {
   background-color: #bfdfff;
 }
 
+div#followlines {
+  display: inline;
+  position: absolute;
+  background-color: #FFFFFF;
+  border: 1px solid #999;
+  color: #000000;
+  padding: 2px;
+}
+
 .sourcelines > a {
     display: inline-block;
     position: absolute;
diff --git a/tests/test-hgweb-commands.t b/tests/test-hgweb-commands.t
--- a/tests/test-hgweb-commands.t
+++ b/tests/test-hgweb-commands.t
@@ -1346,6 +1346,11 @@ File-related
   <pre class="sourcelines stripes4 wrap bottomline">
   <span id="l1">foo</span><a href="#l1"></a></pre>
   </div>
+  
+  <script type="text/javascript">
+      installLineSelect("/log/1/foo");
+  </script>
+  
   </div>
   </div>
   
@@ -1471,6 +1476,11 @@ File-related
   <pre class="sourcelines stripes4 wrap bottomline">
   <span id="l1">another</span><a href="#l1"></a></pre>
   </div>
+  
+  <script type="text/javascript">
+      installLineSelect("/log/2/foo");
+  </script>
+  
   </div>
   </div>
   


More information about the Mercurial-devel mailing list