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

Denis Laxalde denis at laxalde.org
Fri Mar 24 03:57:40 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 b633be53b40e8b2f5debb3a7aa528799072987bf
# Parent  f997405d59861cf460931d519425b361b7aea0fb
# Available At http://hg.logilab.org/users/dlaxalde/hg
#              hg pull http://hg.logilab.org/users/dlaxalde/hg -r b633be53b40e
# EXP-Topic linerange-log/hgweb-filelog
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".

All JavaScript code lives in a new "linerangelog.js" file, sourced in
filerevision template (only in "paper" style for now).

This is proposal implementation, comments welcome on any aspects.

diff --git a/contrib/wix/templates.wxs b/contrib/wix/templates.wxs
--- a/contrib/wix/templates.wxs
+++ b/contrib/wix/templates.wxs
@@ -225,6 +225,7 @@
             <File Id="static.coal.file.png"      Name="coal-file.png" />
             <File Id="static.coal.folder.png"    Name="coal-folder.png" />
             <File Id="static.excanvas.js"        Name="excanvas.js" />
+            <File Id="static.linerangelog.js"    Name="linerangelog.js" />
             <File Id="static.mercurial.js"       Name="mercurial.js" />
             <File Id="static.hgicon.png"         Name="hgicon.png" />
             <File Id="static.hglogo.png"         Name="hglogo.png" />
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
@@ -71,8 +71,11 @@
 <div class="overflow">
 <div class="sourcefirst linewraptoggle">line wrap: <a class="linewraplink" href="javascript:toggleLinewrap()">on</a></div>
 <div class="sourcefirst"> line source</div>
-<pre class="sourcelines stripes4 wrap bottomline">{text%fileline}</pre>
+<pre class="sourcelines stripes4 wrap bottomline" data-logurl="{url|urlescape}log/{symrev}/{file|urlescape}">{text%fileline}</pre>
 </div>
+
+<script type="text/javascript" src="{staticurl|urlescape}linerangelog.js"></script>
+
 </div>
 </div>
 
diff --git a/mercurial/templates/static/linerangelog.js b/mercurial/templates/static/linerangelog.js
new file mode 100644
--- /dev/null
+++ b/mercurial/templates/static/linerangelog.js
@@ -0,0 +1,83 @@
+// Copyright 2017 Logilab SA <contact at logilab.fr>
+//
+// This software may be used and distributed according to the terms
+// of the GNU General Public License, incorporated herein by reference.
+
+document.addEventListener('DOMContentLoaded', function() {
+    // install mouse event listeners on <pre class="sourcelines">
+    var sourcelines = document.getElementsByClassName('sourcelines')[0];
+    if (typeof sourcelines === 'undefined') {
+        return;
+    }
+    var targetUri = sourcelines.dataset.logurl;
+    if (typeof targetUri === '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;
+    }
+
+}, 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
@@ -1343,9 +1343,12 @@ File-related
   <div class="overflow">
   <div class="sourcefirst linewraptoggle">line wrap: <a class="linewraplink" href="javascript:toggleLinewrap()">on</a></div>
   <div class="sourcefirst"> line source</div>
-  <pre class="sourcelines stripes4 wrap bottomline">
+  <pre class="sourcelines stripes4 wrap bottomline" data-logurl="/log/1/foo">
   <span id="l1">foo</span><a href="#l1"></a></pre>
   </div>
+  
+  <script type="text/javascript" src="/static/linerangelog.js"></script>
+  
   </div>
   </div>
   
@@ -1468,9 +1471,12 @@ File-related
   <div class="overflow">
   <div class="sourcefirst linewraptoggle">line wrap: <a class="linewraplink" href="javascript:toggleLinewrap()">on</a></div>
   <div class="sourcefirst"> line source</div>
-  <pre class="sourcelines stripes4 wrap bottomline">
+  <pre class="sourcelines stripes4 wrap bottomline" data-logurl="/log/2/foo">
   <span id="l1">another</span><a href="#l1"></a></pre>
   </div>
+  
+  <script type="text/javascript" src="/static/linerangelog.js"></script>
+  
   </div>
   </div>
   


More information about the Mercurial-devel mailing list