Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Issue 3660: Cycle completions with Page Up/Down and Home/End

Also actually saves the scrolling state of the completion popup.

http://code.google.com/p/fbug/issues/detail?id=3660
  • Loading branch information...
commit 3fb6d30c1208d1215ddb2a3400970e85f25f95c4 1 parent 6c017e1
Simon Lindholm authored May 24, 2012
168  extension/content/firebug/console/autoCompleter.js
@@ -31,10 +31,14 @@ const reLiteralExpr = /^[ "0-9,]*$/;
31 31
 
32 32
 Firebug.JSAutoCompleter = function(textBox, completionBox, options)
33 33
 {
  34
+    var popupSize = 40;
  35
+
34 36
     this.textBox = textBox;
35  
-    this.completionBox = completionBox;
36 37
     this.options = options;
37 38
 
  39
+    this.completionBox = completionBox;
  40
+    this.popupTop = this.popupBottom = null;
  41
+
38 42
     this.completionBase = {
39 43
         pre: null,
40 44
         expr: null,
@@ -77,7 +81,7 @@ Firebug.JSAutoCompleter = function(textBox, completionBox, options)
77 81
         };
78 82
         this.completions = null;
79 83
 
80  
-        this.showCompletions();
  84
+        this.showCompletions(false);
81 85
     };
82 86
 
83 87
     /**
@@ -90,7 +94,7 @@ Firebug.JSAutoCompleter = function(textBox, completionBox, options)
90 94
         this.completionBase.candidates = [];
91 95
         this.completions = null;
92 96
 
93  
-        this.showCompletions();
  97
+        this.showCompletions(false);
94 98
     };
95 99
 
96 100
     /**
@@ -121,7 +125,7 @@ Firebug.JSAutoCompleter = function(textBox, completionBox, options)
121 125
     {
122 126
         this.revertValue = null;
123 127
         this.createCandidates(context);
124  
-        this.showCompletions();
  128
+        this.showCompletions(false);
125 129
     };
126 130
 
127 131
     /**
@@ -253,7 +257,7 @@ Firebug.JSAutoCompleter = function(textBox, completionBox, options)
253 257
             this.completions.index = 0;
254 258
         else if (this.completions.index < 0)
255 259
             this.completions.index = this.completions.list.length - 1;
256  
-        this.showCompletions();
  260
+        this.showCompletions(true);
257 261
     };
258 262
 
259 263
     /**
@@ -287,16 +291,17 @@ Firebug.JSAutoCompleter = function(textBox, completionBox, options)
287 291
 
288 292
     /**
289 293
      * Update the completion box and popup to be consistent with the current
290  
-     * state of the auto-completer.
  294
+     * state of the auto-completer. If just cycling, the old scolling state
  295
+     * for the popup is preserved.
291 296
      */
292  
-    this.showCompletions = function()
  297
+    this.showCompletions = function(cycling)
293 298
     {
294 299
         this.completionBox.value = this.getCompletionBoxValue();
295 300
 
296 301
         var show = this.showCompletionPopup ||
297 302
             (this.completionPopup && this.completionPopup.state === "open");
298 303
         if (show && this.completions && this.completions.list.length > 1)
299  
-            this.popupCandidates();
  304
+            this.popupCandidates(cycling);
300 305
         else
301 306
             this.closePopup();
302 307
     };
@@ -369,11 +374,32 @@ Firebug.JSAutoCompleter = function(textBox, completionBox, options)
369 374
                 }
370 375
             }
371 376
         }
372  
-        else if (event.keyCode === KeyEvent.DOM_VK_UP || event.keyCode === KeyEvent.DOM_VK_DOWN)
  377
+        else if (event.keyCode === KeyEvent.DOM_VK_UP ||
  378
+            event.keyCode === KeyEvent.DOM_VK_DOWN)
373 379
         {
374 380
             if (this.completions)
375 381
             {
376  
-                this.cycle((event.keyCode === KeyEvent.DOM_VK_UP ? -1 : 1));
  382
+                this.cycle(event.keyCode === KeyEvent.DOM_VK_UP ? -1 : 1);
  383
+                Events.cancelEvent(event);
  384
+                return true;
  385
+            }
  386
+        }
  387
+        else if (event.keyCode === KeyEvent.DOM_VK_PAGE_UP ||
  388
+            event.keyCode === KeyEvent.DOM_VK_PAGE_DOWN)
  389
+        {
  390
+            if (this.completions)
  391
+            {
  392
+                this.pageCycle(event.keyCode === KeyEvent.DOM_VK_PAGE_UP ? -1 : 1);
  393
+                Events.cancelEvent(event);
  394
+                return true;
  395
+            }
  396
+        }
  397
+        else if (event.keyCode === KeyEvent.DOM_VK_HOME ||
  398
+            event.keyCode === KeyEvent.DOM_VK_END)
  399
+        {
  400
+            if (this.completions)
  401
+            {
  402
+                this.topCycle(event.keyCode === KeyEvent.DOM_VK_HOME ? -1 : 1);
377 403
                 Events.cancelEvent(event);
378 404
                 return true;
379 405
             }
@@ -405,7 +431,7 @@ Firebug.JSAutoCompleter = function(textBox, completionBox, options)
405 431
                 this.complete(context);
406 432
             }
407 433
             if (this.completionPopup && this.completions)
408  
-                this.popupCandidates();
  434
+                this.popupCandidates(false);
409 435
         }
410 436
     };
411 437
 
@@ -445,10 +471,65 @@ Firebug.JSAutoCompleter = function(textBox, completionBox, options)
445 471
         this.revertValue = originalValue;
446 472
     };
447 473
 
448  
-    this.popupCandidates = function()
  474
+    this.pageCycle = function(dir)
449 475
     {
450  
-        var commandCompletionLineLimit = 40;
  476
+        var list = this.completions.list, selIndex = this.completions.index;
  477
+
  478
+        if (!this.isPopupOpen())
  479
+        {
  480
+            // When no popup is open, cycle by a fixed amount and stop at edges.
  481
+            selIndex += dir * 15;
  482
+            selIndex = Math.max(selIndex, 0);
  483
+            selIndex = Math.min(selIndex, list.length-1);
  484
+            this.completions.index = selIndex;
  485
+            this.showCompletions(true);
  486
+            return;
  487
+        }
451 488
 
  489
+        var top = this.popupTop, bottom = this.popupBottom;
  490
+        if (top === 0 && bottom === list.length)
  491
+        {
  492
+            // For a single scroll page, act like home/end.
  493
+            this.topCycle(dir);
  494
+            return;
  495
+        }
  496
+
  497
+        var immediateTarget;
  498
+        if (dir === -1)
  499
+            immediateTarget = (top === 0 ? 0 : top + 2);
  500
+        else
  501
+            immediateTarget = (bottom === list.length ? bottom: bottom - 2) - 1;
  502
+        if ((selIndex - immediateTarget) * dir < 0)
  503
+        {
  504
+            // The selection has not yet reached the edge target, so jump to it.
  505
+            selIndex = immediateTarget;
  506
+        }
  507
+        else
  508
+        {
  509
+            // Show the next page.
  510
+            if (dir === -1 && top - popupSize <= 0)
  511
+                selIndex = 0;
  512
+            else if (dir === 1 && bottom + popupSize >= list.length)
  513
+                selIndex = list.length - 1;
  514
+            else
  515
+                selIndex = immediateTarget + dir*popupSize;
  516
+        }
  517
+
  518
+        this.completions.index = selIndex;
  519
+        this.showCompletions(true);
  520
+    };
  521
+
  522
+    this.topCycle = function(dir)
  523
+    {
  524
+        if (dir === -1)
  525
+            this.completions.index = 0;
  526
+        else
  527
+            this.completions.index = this.completions.list.length - 1;
  528
+        this.showCompletions(true);
  529
+    };
  530
+
  531
+    this.popupCandidates = function(cycling)
  532
+    {
452 533
         Dom.eraseNode(this.completionPopup);
453 534
         this.selectedPopupElement = null;
454 535
 
@@ -464,28 +545,52 @@ Firebug.JSAutoCompleter = function(textBox, completionBox, options)
464 545
 
465 546
         var escPrefix = Str.escapeForTextNode(this.textBox.value);
466 547
 
467  
-        var showTop = 0;
468  
-        var showBottom = this.completions.list.length;
469  
-        if (this.completions.list.length > commandCompletionLineLimit)
  548
+        var list = this.completions.list, selIndex = this.completions.index;
  549
+
  550
+        if (this.completions.list.length <= popupSize)
  551
+        {
  552
+            this.popupTop = 0;
  553
+            this.popupBottom = list.length;
  554
+        }
  555
+        else
470 556
         {
471  
-            if (this.completions.index <= (commandCompletionLineLimit - 3))
  557
+            var self = this;
  558
+            var setTop = function(val)
  559
+            {
  560
+                if (val < 0)
  561
+                    val = 0;
  562
+                self.popupTop = val;
  563
+                self.popupBottom = val + popupSize;
  564
+                if (self.popupBottom > list.length)
  565
+                    setBottom(list.length);
  566
+            };
  567
+            var setBottom = function(val)
  568
+            {
  569
+                if (val > list.length)
  570
+                    val = list.length;
  571
+                self.popupBottom = val;
  572
+                self.popupTop = val - popupSize;
  573
+                if (self.popupTop < 0)
  574
+                    setTop(0);
  575
+            };
  576
+
  577
+            if (!cycling)
472 578
             {
473  
-                // We are in the top part of the list.
474  
-                showBottom = commandCompletionLineLimit;
  579
+                // Show the selection at nearly the bottom of the popup, where
  580
+                // it is more local.
  581
+                setBottom(selIndex + 3);
475 582
             }
476 583
             else
477 584
             {
478  
-                // Implement manual scrolling.
479  
-                if (this.completions.index > (this.completions.list.length - 3))
480  
-                    showBottom = this.completions.list.length;
481  
-                else
482  
-                    showBottom = this.completions.index + 3;
  585
+                // Scroll the popup such that selIndex fits.
  586
+                if (selIndex - 2 < this.popupTop)
  587
+                    setTop(selIndex - 2);
  588
+                else if (selIndex + 3 > this.popupBottom)
  589
+                    setBottom(selIndex + 3);
483 590
             }
484  
-
485  
-            showTop = showBottom - commandCompletionLineLimit;
486 591
         }
487 592
 
488  
-        for (var i = showTop; i < showBottom; i++)
  593
+        for (var i = this.popupTop; i < this.popupBottom; i++)
489 594
         {
490 595
             var hbox = this.completionPopup.ownerDocument.
491 596
                 createElementNS("http://www.w3.org/1999/xhtml","div");
@@ -502,7 +607,7 @@ Firebug.JSAutoCompleter = function(textBox, completionBox, options)
502 607
             post.innerHTML = Str.escapeForTextNode(completion);
503 608
             post.classList.add("completionText");
504 609
 
505  
-            if (i === this.completions.index)
  610
+            if (i === selIndex)
506 611
                 this.selectedPopupElement = hbox;
507 612
 
508 613
             hbox.appendChild(pre);
@@ -516,9 +621,14 @@ Firebug.JSAutoCompleter = function(textBox, completionBox, options)
516 621
         this.completionPopup.openPopup(this.textBox, "before_start", 0, 0, false, false);
517 622
     };
518 623
 
  624
+    this.isPopupOpen = function()
  625
+    {
  626
+        return (this.completionPopup && this.completionPopup.state !== "closed");
  627
+    };
  628
+
519 629
     this.closePopup = function()
520 630
     {
521  
-        if (!this.completionPopup || this.completionPopup.state === "closed")
  631
+        if (!this.isPopupOpen())
522 632
             return;
523 633
 
524 634
         try

0 notes on commit 3fb6d30

Please sign in to comment.
Something went wrong with that request. Please try again.