@@ -17,14 +17,18 @@ class Readline extends EventEmitter implements ReadableStreamInterface
17
17
private $ echo = true ;
18
18
private $ autocomplete = null ;
19
19
private $ move = true ;
20
- private $ history = null ;
21
20
private $ encoding = 'utf-8 ' ;
22
21
23
22
private $ input ;
24
23
private $ output ;
25
24
private $ sequencer ;
26
25
private $ closed = false ;
27
26
27
+ private $ historyLines = array ();
28
+ private $ historyPosition = null ;
29
+ private $ historyUnsaved = null ;
30
+ private $ historyLimit = 500 ;
31
+
28
32
public function __construct (ReadableStreamInterface $ input , WritableStreamInterface $ output )
29
33
{
30
34
$ this ->input = $ input ;
@@ -311,17 +315,73 @@ public function getInput()
311
315
}
312
316
313
317
/**
314
- * set history handler to use (or none)
318
+ * Adds a new line to the (bottom position of the) history list
315
319
*
316
- * The history handler will be called whenever the user hits the UP or DOWN
317
- * arrow keys.
320
+ * @param string $line
321
+ * @return self
322
+ * @uses self::limitHistory() to make sure list does not exceed limits
323
+ */
324
+ public function addHistory ($ line )
325
+ {
326
+ $ this ->historyLines []= $ line ;
327
+
328
+ return $ this ->limitHistory ($ this ->historyLimit );
329
+ }
330
+
331
+ /**
332
+ * Clears the complete history list
318
333
*
319
- * @param HistoryInterface|null $history
320
334
* @return self
321
335
*/
322
- public function setHistory (HistoryInterface $ history = null )
336
+ public function clearHistory ()
337
+ {
338
+ $ this ->historyLines = array ();
339
+ $ this ->historyPosition = null ;
340
+
341
+ if ($ this ->historyUnsaved !== null ) {
342
+ $ this ->setInput ($ this ->historyUnsaved );
343
+ $ this ->historyUnsaved = null ;
344
+ }
345
+
346
+ return $ this ;
347
+ }
348
+
349
+ /**
350
+ * Returns an array with all lines in the history
351
+ *
352
+ * @return string[]
353
+ */
354
+ public function listHistory ()
323
355
{
324
- $ this ->history = $ history ;
356
+ return $ this ->historyLines ;
357
+ }
358
+
359
+ /**
360
+ * Limits the history to a maximum of N entries and truncates the current history list accordingly
361
+ *
362
+ * @param int|null $limit
363
+ * @return self
364
+ */
365
+ public function limitHistory ($ limit )
366
+ {
367
+ $ this ->historyLimit = $ limit === null ? null : (int )$ limit ;
368
+
369
+ // limit send and currently exceeded
370
+ if ($ this ->historyLimit !== null && isset ($ this ->historyLines [$ this ->historyLimit ])) {
371
+ // adjust position in history according to new position after applying limit
372
+ if ($ this ->historyPosition !== null ) {
373
+ $ this ->historyPosition -= count ($ this ->historyLines ) - $ this ->historyLimit ;
374
+
375
+ // current position will drop off from list => restore original
376
+ if ($ this ->historyPosition < 0 ) {
377
+ $ this ->setInput ($ this ->historyUnsaved );
378
+ $ this ->historyPosition = null ;
379
+ $ this ->historyUnsaved = null ;
380
+ }
381
+ }
382
+
383
+ $ this ->historyLines = array_slice ($ this ->historyLines , -$ this ->historyLimit , $ this ->historyLimit );
384
+ }
325
385
326
386
return $ this ;
327
387
}
@@ -468,16 +528,40 @@ public function onKeyRight()
468
528
/** @internal */
469
529
public function onKeyUp ()
470
530
{
471
- if ($ this ->history !== null ) {
472
- $ this ->history ->up ();
531
+ // ignore if already at top or history is empty
532
+ if ($ this ->historyPosition === 0 || !$ this ->historyLines ) {
533
+ return ;
534
+ }
535
+
536
+ if ($ this ->historyPosition === null ) {
537
+ // first time up => move to last entry
538
+ $ this ->historyPosition = count ($ this ->historyLines ) - 1 ;
539
+ $ this ->historyUnsaved = $ this ->getInput ();
540
+ } else {
541
+ // somewhere in the list => move by one
542
+ $ this ->historyPosition --;
473
543
}
544
+
545
+ $ this ->setInput ($ this ->historyLines [$ this ->historyPosition ]);
474
546
}
475
547
476
548
/** @internal */
477
549
public function onKeyDown ()
478
550
{
479
- if ($ this ->history !== null ) {
480
- $ this ->history ->down ();
551
+ // ignore if not currently cycling through history
552
+ if ($ this ->historyPosition === null ) {
553
+ return ;
554
+ }
555
+
556
+ if (isset ($ this ->historyLines [$ this ->historyPosition + 1 ])) {
557
+ // this is still a valid position => advance by one and apply
558
+ $ this ->historyPosition ++;
559
+ $ this ->setInput ($ this ->historyLines [$ this ->historyPosition ]);
560
+ } else {
561
+ // moved beyond bottom => restore original unsaved input
562
+ $ this ->setInput ($ this ->historyUnsaved );
563
+ $ this ->historyPosition = null ;
564
+ $ this ->historyUnsaved = null ;
481
565
}
482
566
}
483
567
@@ -537,6 +621,10 @@ public function deleteChar($n)
537
621
*/
538
622
protected function processLine ()
539
623
{
624
+ // reset history cycle position
625
+ $ this ->historyPosition = null ;
626
+ $ this ->historyUnsaved = null ;
627
+
540
628
// store and reset/clear/redraw current input
541
629
$ line = $ this ->linebuffer ;
542
630
if ($ line !== '' ) {
@@ -548,9 +636,6 @@ protected function processLine()
548
636
}
549
637
550
638
// process stored input buffer
551
- if ($ this ->history !== null ) {
552
- $ this ->history ->addLine ($ line );
553
- }
554
639
$ this ->emit ('data ' , array ($ line ));
555
640
}
556
641
0 commit comments