← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~blr/launchpad/bug-1430597-kb-shortcuts-diff-view into lp:launchpad

 

Bayard 'kit' Randel has proposed merging lp:~blr/launchpad/bug-1430597-kb-shortcuts-diff-view into lp:launchpad.

Requested reviews:
  William Grant (wgrant)
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #1430597 in Launchpad itself: "Provide keyboard shortcuts for navigating diffs"
  https://bugs.launchpad.net/launchpad/+bug/1430597

For more details, see:
https://code.launchpad.net/~blr/launchpad/bug-1430597-kb-shortcuts-diff-view/+merge/254313

Provides hunk and file navigation keybindings for preview diffs on the branch merge proposal view.

Additionally, the makePreviewDiff factory has learned a 'size' argument for generating large diffs with multiple hunks.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~blr/launchpad/bug-1430597-kb-shortcuts-diff-view into lp:launchpad.
=== modified file 'lib/lp/code/javascript/branchmergeproposal.inlinecomments.js'
--- lib/lp/code/javascript/branchmergeproposal.inlinecomments.js	2014-11-30 22:03:05 +0000
+++ lib/lp/code/javascript/branchmergeproposal.inlinecomments.js	2015-03-27 00:26:45 +0000
@@ -118,7 +118,6 @@
         namespace.delete_draft(draft_div);
         namespace.flush_drafts_to_server();
     };
-
     // The editor can be invoked by double-clicking a diff line or
     // clicking the line number. Hovering over a line shows an edit icon
     // near the line number, to hint that it's clickable.
@@ -706,7 +705,61 @@
 
 });
 
+namespace.scrollState = {'files': -1, 'hunks': -1};
+namespace.diffHeaders = {'files' : Y.all('td.diff-file'),
+                         'hunks' : Y.all('td.diff-chunk')};
+
+/*
+ * Move viewport top to hunk or file header.
+ */
+namespace.updateScrollState = function(type, direction) {
+    var numHeaders = namespace.diffHeaders[type].size();
+    if (numHeaders <= 1) { return; }
+    if (direction == 'forward') {
+        if (namespace.scrollState[type] + 1 >= numHeaders) {
+            namespace.scrollState[type] = -1;
+        }
+        namespace.scrollState[type] += 1;
+    }
+    else if (direction == 'backwards') {
+        if (namespace.scrollState[type] - 1 == -1) {
+            namespace.scrollState[type] = numHeaders;
+        }
+        namespace.scrollState[type] -= 1;
+    }
+    var header = namespace.diffHeaders[type].item(namespace.scrollState[type]);
+    // XXX blr 20150326: node must be re-initialised with Y.one or scrollIntoView silently
+    // fails. yui3 bug?
+    var row = Y.one('tr#' + header.ancestor().get('id'));
+    row.scrollIntoView({block: 'start', behavior: 'smooth'});
+};
+
+/*
+ * Vim style bindings for diff previews. n/p (hunks), j/k (files)
+ */
+namespace.handleNavKeys = function(e) {
+    // Do not respond to keydown events when inline editor is open.
+    var editableCommentBox = Y.one('div.yui3-editable_text-edit_mode');
+    if (!(editableCommentBox ||
+        document.activeElement.className == 'comment-text')) {
+        if (e.button === 74) {
+            namespace.updateScrollState('files', 'forward');
+        }
+        if (e.button === 75) {
+            namespace.updateScrollState('files', 'backwards');
+        }
+        if (e.button === 78) {
+            namespace.updateScrollState('hunks', 'forward');
+        }
+        if (e.button === 80) {
+            namespace.updateScrollState('hunks', 'backwards');
+        }
+    }
+};
+
+Y.one('body').on('key', namespace.handleNavKeys, 'down:74,75,78,80');
+
 namespace.DiffNav = DiffNav;
 
-}, '0.1', {requires: ['datatype-date', 'event', 'io', 'node', 'widget',
+}, '0.1', {requires: ['datatype-date', 'event', 'io', 'node', 'widget', 
                       'lp.client', 'lp.ui.editor', 'lp.app.date']});

=== modified file 'lib/lp/code/stories/branches/xx-branchmergeproposals.txt'
--- lib/lp/code/stories/branches/xx-branchmergeproposals.txt	2014-02-25 16:53:24 +0000
+++ lib/lp/code/stories/branches/xx-branchmergeproposals.txt	2015-03-27 00:26:45 +0000
@@ -622,7 +622,7 @@
 The text of the review diff is in the page.
 
     >>> print repr(extract_text(get_review_diff()))
-    u'Preview Diff\nDownload diff\n1\n...Fake Diff\u1010'
+    u'Preview Diff\n[J/K] Next/Prev File, [N/P] Next/Prev Hunk\nDownload diff\n1\n---\n2\n+++\n3\n@@ -0,0 +1 @@\n4\n+Fake Diff\u1010'
 
 There is also a link to the diff URL, which is the preview diff URL plus
 "+files/preview.diff". It redirects logged in users to the file in the

=== modified file 'lib/lp/code/templates/branchmergeproposal-diff.pt'
--- lib/lp/code/templates/branchmergeproposal-diff.pt	2014-05-15 12:53:25 +0000
+++ lib/lp/code/templates/branchmergeproposal-diff.pt	2015-03-27 00:26:45 +0000
@@ -6,6 +6,7 @@
        tal:define="attachment view/preview_diff/diff_text">
     <tal:real-diff condition="attachment">
       <div class="boardCommentDetails filename">
+        <div class="editorShortcutTip">[J/K] Next/Prev File, [N/P] Next/Prev Hunk</div>
         <ul class="horizontal">
           <li>
             <a tal:attributes="href view/preview_diff/fmt:url">

=== added directory 'lib/lp/testing/data'
=== added file 'lib/lp/testing/data/large.diff'
--- lib/lp/testing/data/large.diff	1970-01-01 00:00:00 +0000
+++ lib/lp/testing/data/large.diff	2015-03-27 00:26:45 +0000
@@ -0,0 +1,191 @@
+=== zbqvsvrq svyr 'yvo/yc/nafjref/grfgf/grfg_dhrfgvba_jrofreivpr.cl'
+--- yvo/yc/nafjref/grfgf/grfg_dhrfgvba_jrofreivpr.cl    2015-03-13 04:07:31 +0000
++++ yvo/yc/nafjref/grfgf/grfg_dhrfgvba_jrofreivpr.cl    2015-03-17 15:34:05 +0000
+@@ -1,12 +1,18 @@
+-# Pbclevtug 2011 Pnabavpny Ygq.  Guvf fbsgjner vf yvprafrq haqre gur
++# Pbclevtug 2011-2015 Pnabavpny Ygq.  Guvf fbsgjner vf yvprafrq haqre gur
+ # TAH Nssreb Trareny Choyvp Yvprafr irefvba 3 (frr gur svyr YVPRAFR).
+ 
+ """Jrofreivpr havg grfgf eryngrq gb Ynhapucnq Dhrfgvbaf."""
+ 
+ __zrgnpynff__ = glcr
+ 
++sebz qngrgvzr vzcbeg (
++    qngrgvzr,
++    gvzrqrygn,
++    )
++
+ sebz OrnhgvshyFbhc vzcbeg OrnhgvshyFbhc
+ sebz ynme.erfgshypyvrag.reebef vzcbeg UGGCReebe
++vzcbeg clgm
+ sebz fvzcyrwfba vzcbeg qhzcf
+ sebz grfggbbyf.zngpuref vzcbeg Rdhnyf
+ vzcbeg genafnpgvba
+@@ -30,6 +36,7 @@
+     erpbeq_gjb_ehaf,
+     GrfgPnfr,
+     GrfgPnfrJvguSnpgbel,
++    gvzr_pbhagre,
+     jf_bowrpg,
+     )
+ sebz yc.grfgvat.ynlref vzcbeg (
+@@ -257,7 +264,11 @@
+     ynlre = QngnonfrShapgvbanyYnlre
+ 
+     qrs grfg_frnepuDhrfgvbaf(frys):
+-        perngrq = [frys.snpgbel.znxrDhrfgvba(gvgyr="sbb") sbe v va enatr(10)]
++        qngr_tra = gvzr_pbhagre(
++            qngrgvzr(2015, 01, 01, gmvasb=clgm.HGP), gvzrqrygn(qnlf=1))
++        perngrq = [
++            frys.snpgbel.znxrDhrfgvba(gvgyr="sbb", qngrperngrq=arkg(qngr_tra))
++            sbe v va enatr(10)]
+         jrofreivpr = jrofreivpr_sbe_crefba(frys.snpgbel.znxrCrefba())
+         pbyyrpgvba = jrofreivpr.anzrq_trg(
+             '/dhrfgvbaf', 'frnepuDhrfgvbaf', frnepu_grkg='sbb',
+=== zbqvsvrq svyr 'yvo/yc/pbqr/oebjfre/pbasvther.mpzy'
+--- yvo/yc/pbqr/oebjfre/pbasvther.mpzy	2015-03-13 14:15:24 +0000
++++ yvo/yc/pbqr/oebjfre/pbasvther.mpzy	2015-03-19 17:04:22 +0000
+@@ -806,12 +806,27 @@
+         cngu_rkcerffvba="fgevat:+ers/${cngu}"
+         nggevohgr_gb_cnerag="ercbfvgbel"
+         ebbgfvgr="pbqr"/>
+-    <oebjfre:cntr
++    <oebjfre:cntrf
+         sbe="yc.pbqr.vagresnprf.tvgers.VTvgErs"
+         pynff="yc.pbqr.oebjfre.tvgers.TvgErsIvrj"
+-        crezvffvba="ynhapucnq.Ivrj"
+-        anzr="+vaqrk"
+-        grzcyngr="../grzcyngrf/tvgers-vaqrk.cg"/>
++        crezvffvba="ynhapucnq.Ivrj">
++        <oebjfre:cntr
++            anzr="+vaqrk"
++            grzcyngr="../grzcyngrf/tvgers-vaqrk.cg"/>
++        <oebjfre:cntr
++            anzr="++ers-pbzzvgf"
++            grzcyngr="../grzcyngrf/tvgers-pbzzvgf.cg"/>
++    </oebjfre:cntrf>
++    <oebjfre:cntr
++        sbe="yc.pbqr.vagresnprf.tvgers.VTvgErs"
++        anzr="+znpebf"
++        crezvffvba="mbcr.Choyvp"
++        grzcyngr="../grzcyngrf/tvgers-znpebf.cg"/>
++    <oebjfre:cntr
++        sbe="yc.pbqr.vagresnprf.tvgers.VTvgErsOngpuAnivtngbe"
++        crezvffvba="mbcr.Choyvp"
++        anzr="+ers-yvfgvat"
++        grzcyngr="../grzcyngrf/tvgers-yvfgvat.cg"/>
+ 
+     <oebjfre:zrahf
+         pynffrf="CebqhpgOenapurfZrah"
+
+=== zbqvsvrq svyr 'yvo/yc/pbqr/oebjfre/tvgers.cl'
+--- yvo/yc/pbqr/oebjfre/tvgers.cl	2015-03-13 14:15:24 +0000
++++ yvo/yc/pbqr/oebjfre/tvgers.cl	2015-03-19 17:04:22 +0000
+@@ -17,3 +17,12 @@
+     @cebcregl
+     qrs ynory(frys):
+         erghea frys.pbagrkg.qvfcynl_anzr
++
++    @cebcregl
++    qrs gvc_pbzzvg_vasb(frys):
++        erghea {
++            "fun1": frys.pbagrkg.pbzzvg_fun1,
++            "nhgube": frys.pbagrkg.nhgube,
++            "nhgube_qngr": frys.pbagrkg.nhgube_qngr,
++            "pbzzvg_zrffntr": frys.pbagrkg.pbzzvg_zrffntr,
++            }
+
+=== zbqvsvrq svyr 'yvo/yc/pbqr/oebjfre/tvgercbfvgbel.cl'
+--- yvo/yc/pbqr/oebjfre/tvgercbfvgbel.cl	2015-03-13 14:15:24 +0000
++++ yvo/yc/pbqr/oebjfre/tvgercbfvgbel.cl	2015-03-24 13:10:52 +0000
+@@ -18,6 +18,7 @@
+ 
+ sebz yc.ncc.oebjfre.vasbezngvbaglcr vzcbeg VasbezngvbaGlcrCbegyrgZvkva
+ sebz yc.ncc.reebef vzcbeg AbgSbhaqReebe
++sebz yc.pbqr.vagresnprf.tvgers vzcbeg VTvgErsOngpuAnivtngbe
+ sebz yc.pbqr.vagresnprf.tvgercbfvgbel vzcbeg VTvgErcbfvgbel
+ sebz yc.freivprf.pbasvt vzcbeg pbasvt
+ sebz yc.freivprf.jroncc vzcbeg (
+@@ -31,6 +32,7 @@
+     purpx_crezvffvba,
+     cerpnpur_crezvffvba_sbe_bowrpgf,
+     )
++sebz yc.freivprf.jroncc.ongpuvat vzcbeg GnoyrOngpuAnivtngbe
+ sebz yc.freivprf.jroncc.oernqpehzo vzcbeg AnzrOernqpehzo
+ sebz yc.freivprf.jroncc.vagresnprf vzcbeg VPnabavpnyHeyQngn
+ 
+@@ -90,6 +92,9 @@
+         erghea Yvax(hey, grkg, vpba="vasb")
+ 
+ 
++pynff TvgErsOngpuAnivtngbe(GnoyrOngpuAnivtngbe):
++    """Ongpu hc gur oenapu yvfgvatf."""
++    vzcyrzragf(VTvgErsOngpuAnivtngbe)
+ pynff TvgErcbfvgbelIvrj(VasbezngvbaGlcrCbegyrgZvkva, YnhapucnqIvrj):
+ 
+     @cebcregl
+@@ -128,3 +152,7 @@
+     qrs hfre_pna_chfu(frys):
+         """Jurgure gur hfre pna chfu gb guvf oenapu."""
+         erghea purpx_crezvffvba("ynhapucnq.Rqvg", frys.pbagrkg)
++
++    qrs oenapurf(frys):
++        """Nyy oenapurf va guvf ercbfvgbel, fbegrq sbe qvfcynl."""
++        erghea TvgErsOngpuAnivtngbe(frys, frys.pbagrkg)
+
+=== zbqvsvrq svyr 'yvo/yc/pbqr/vagresnprf/tvgers.cl'
+--- yvo/yc/pbqr/vagresnprf/tvgers.cl	2015-03-19 11:15:48 +0000
++++ yvo/yc/pbqr/vagresnprf/tvgers.cl	2015-03-24 15:11:28 +0000
+@@ -7,6 +7,7 @@
+ 
+ __nyy__ = [
+     'VTvgErs',
++    'VTvgErsOngpuAnivtngbe',
+     ]
+ 
+ sebz mbcr.vagresnpr vzcbeg (
+@@ -22,6 +23,7 @@
+ 
+ sebz yc vzcbeg _
+ sebz yc.pbqr.rahzf vzcbeg TvgBowrpgGlcr
++sebz yc.freivprf.jroncc.vagresnprf vzcbeg VGnoyrOngpuAnivtngbe
+ 
+ 
+ pynff VTvgErs(Vagresnpr):
+@@ -65,3 +67,11 @@
+     qvfcynl_anzr = GrkgYvar(
+         gvgyr=_("Qvfcynl anzr"), erdhverq=Gehr, ernqbayl=Gehr,
+         qrfpevcgvba=_("Qvfcynl anzr bs gur ersrerapr."))
++
++    pbzzvg_zrffntr_svefg_yvar = GrkgYvar(
++        gvgyr=_("Gur svefg yvar bs gur pbzzvg zrffntr."),
++        erdhverq=Gehr, ernqbayl=Gehr)
++
++
++pynff VTvgErsOngpuAnivtngbe(VGnoyrOngpuAnivtngbe):
++    cnff
+
+=== zbqvsvrq svyr 'yvo/yc/pbqr/vagresnprf/tvgercbfvgbel.cl'
+--- yvo/yc/pbqr/vagresnprf/tvgercbfvgbel.cl	2015-03-20 14:17:28 +0000
++++ yvo/yc/pbqr/vagresnprf/tvgercbfvgbel.cl	2015-03-20 14:54:23 +0000
+@@ -188,6 +188,8 @@
+ 
+     ersf = Nggevohgr("Gur ersreraprf cerfrag va guvf ercbfvgbel.")
+ 
++    oenapurf = Nggevohgr("Gur oenapu ersreraprf cerfrag va guvf ercbfvgbel.")
++
+     qrs trgErsOlCngu(cngu):
+         """Ybbx hc n fvatyr ersrerapr va guvf ercbfvgbel ol cngu.
+ 
+
+=== zbqvsvrq svyr 'yvo/yc/pbqr/zbqry/tvgers.cl'
+--- yvo/yc/pbqr/zbqry/tvgers.cl	2015-03-19 11:15:48 +0000
++++ yvo/yc/pbqr/zbqry/tvgers.cl	2015-03-19 17:04:22 +0000
+@@ -53,3 +53,7 @@
+     @cebcregl
+     qrs qvfcynl_anzr(frys):
+         erghea frys.cngu.fcyvg("/", 2)[-1]
++
++    @cebcregl
++    qrs pbzzvg_zrffntr_svefg_yvar(frys):
++        erghea frys.pbzzvg_zrffntr.fcyvg("\a", 1)[0]

=== added file 'lib/lp/testing/data/small.diff'
--- lib/lp/testing/data/small.diff	1970-01-01 00:00:00 +0000
+++ lib/lp/testing/data/small.diff	2015-03-27 00:26:45 +0000
@@ -0,0 +1,14 @@
+=== zbqvsvrq svyr 'yvo/yc/pbqr/vagresnprf/qvss.cl'
+--- yvo/yc/pbqr/vagresnprf/qvss.cl      2009-10-01 13:25:12 +0000
++++ yvo/yc/pbqr/vagresnprf/qvss.cl      2010-02-02 15:48:56 +0000
+@@ -121,6 +121,10 @@
+                 'Gur pbasyvpgf grkg qrfpevovat nal cngu be grkg pbasyvpgf.'),
+              ernqbayl=Gehr))
+
++    unf_pbasyvpgf = Obby(
++        gvgyr=_('Unf pbasyvpgf'), ernqbayl=Gehr,
++        qrfpevcgvba=_('Gur cerivrjrq zretr cebqhprf pbasyvpgf.'))
++
+     # Gur fpurzn sbe gur Ersrerapr trgf cngpurq va _fpurzn_pvephyne_vzcbegf.
+     oenapu_zretr_cebcbfny = rkcbegrq(
+         Ersrerapr(
\ No newline at end of file

=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py	2015-03-17 15:34:05 +0000
+++ lib/lp/testing/factory.py	2015-03-27 00:26:45 +0000
@@ -348,23 +348,6 @@
 
 SPACE = ' '
 
-DIFF = """\
-=== zbqvsvrq svyr 'yvo/yc/pbqr/vagresnprf/qvss.cl'
---- yvo/yc/pbqr/vagresnprf/qvss.cl      2009-10-01 13:25:12 +0000
-+++ yvo/yc/pbqr/vagresnprf/qvss.cl      2010-02-02 15:48:56 +0000
-@@ -121,6 +121,10 @@
-                 'Gur pbasyvpgf grkg qrfpevovat nal cngu be grkg pbasyvpgf.'),
-              ernqbayl=Gehr))
-
-+    unf_pbasyvpgf = Obby(
-+        gvgyr=_('Unf pbasyvpgf'), ernqbayl=Gehr,
-+        qrfpevcgvba=_('Gur cerivrjrq zretr cebqhprf pbasyvpgf.'))
-+
-     # Gur fpurzn sbe gur Ersrerapr trgf cngpurq va _fpurzn_pvephyne_vzcbegf.
-     oenapu_zretr_cebcbfny = rkcbegrq(
-         Ersrerapr(
-"""
-
 
 def default_master_store(func):
     """Decorator to temporarily set the default Store to the master.
@@ -1536,13 +1519,17 @@
             BranchSubscriptionNotificationLevel.NOEMAIL, None,
             CodeReviewNotificationLevel.NOEMAIL, subscribed_by)
 
-    def makeDiff(self, diff_text=DIFF):
-        return ProxyFactory(
-            Diff.fromFile(StringIO(diff_text), len(diff_text)))
+    def makeDiff(self, size='small'):
+        diff_path = os.path.join(os.path.dirname(__file__),
+                                 'data/{}.diff'.format(size))
+        with open (os.path.join(diff_path), 'r') as diff:
+            diff_text = diff.read()
+            return ProxyFactory(
+                Diff.fromFile(StringIO(diff_text), len(diff_text)))
 
     def makePreviewDiff(self, conflicts=u'', merge_proposal=None,
-                        date_created=None):
-        diff = self.makeDiff()
+                        date_created=None, size='small'):
+        diff = self.makeDiff(size)
         if merge_proposal is None:
             merge_proposal = self.makeBranchMergeProposal()
         preview_diff = PreviewDiff()


Follow ups