6
6
use React \Stream \ReadableStreamInterface ;
7
7
use React \Stream \WritableStreamInterface ;
8
8
use React \Stream \Util ;
9
+ use Clue \React \Utf8 \Sequencer as Utf8Sequencer ;
10
+ use Clue \React \Term \ControlCodeParser ;
9
11
10
12
class Readline extends EventEmitter implements ReadableStreamInterface
11
13
{
12
- const KEY_BACKSPACE = "\x7f" ;
13
- const KEY_ENTER = "\n" ;
14
- const KEY_TAB = "\t" ;
15
-
16
- const ESC_SEQUENCE = "\033[ " ;
17
- const ESC_LEFT = "D " ;
18
- const ESC_RIGHT = "C " ;
19
- const ESC_UP = "A " ;
20
- const ESC_DOWN = "B " ;
21
- const ESC_HOME = "1~ " ;
22
- const ESC_INS = "2~ " ;
23
- const ESC_DEL = "3~ " ;
24
- const ESC_END = "4~ " ;
25
-
26
- const ESC_F10 = "20~ " ;
27
-
28
14
private $ prompt = '' ;
29
15
private $ linebuffer = '' ;
30
16
private $ linepos = 0 ;
@@ -48,58 +34,46 @@ public function __construct(ReadableStreamInterface $input, WritableStreamInterf
48
34
return $ this ->close ();
49
35
}
50
36
51
- $ this ->sequencer = new Sequencer ();
52
- $ this ->sequencer ->addSequence (self ::KEY_ENTER , array ($ this , 'onKeyEnter ' ));
53
- $ this ->sequencer ->addSequence (self ::KEY_BACKSPACE , array ($ this , 'onKeyBackspace ' ));
54
- $ this ->sequencer ->addSequence (self ::KEY_TAB , array ($ this , 'onKeyTab ' ));
55
-
56
- $ this ->sequencer ->addSequence (self ::ESC_SEQUENCE . self ::ESC_LEFT , array ($ this , 'onKeyLeft ' ));
57
- $ this ->sequencer ->addSequence (self ::ESC_SEQUENCE . self ::ESC_RIGHT , array ($ this , 'onKeyRight ' ));
58
- $ this ->sequencer ->addSequence (self ::ESC_SEQUENCE . self ::ESC_UP , array ($ this , 'onKeyUp ' ));
59
- $ this ->sequencer ->addSequence (self ::ESC_SEQUENCE . self ::ESC_DOWN , array ($ this , 'onKeyDown ' ));
60
- $ this ->sequencer ->addSequence (self ::ESC_SEQUENCE . self ::ESC_HOME , array ($ this , 'onKeyHome ' ));
61
- $ this ->sequencer ->addSequence (self ::ESC_SEQUENCE . self ::ESC_INS , array ($ this , 'onKeyInsert ' ));
62
- $ this ->sequencer ->addSequence (self ::ESC_SEQUENCE . self ::ESC_DEL , array ($ this , 'onKeyDelete ' ));
63
- $ this ->sequencer ->addSequence (self ::ESC_SEQUENCE . self ::ESC_END , array ($ this , 'onKeyEnd ' ));
64
-
65
- $ expect = 0 ;
66
- $ char = '' ;
37
+ // push input through control code parser
38
+ $ parser = new ControlCodeParser ($ input );
39
+
67
40
$ that = $ this ;
68
- $ this ->sequencer ->addFallback ('' , function ($ byte ) use (&$ expect , &$ char , $ that ) {
69
- if ($ expect === 0 ) {
70
- $ code = ord ($ byte );
71
- // count number of bytes expected for this UTF-8 multi-byte character
72
- $ expect = 1 ;
73
- if ($ code & 128 && $ code & 64 ) {
74
- ++$ expect ;
75
- if ($ code & 32 ) {
76
- ++$ expect ;
77
- if ($ code & 16 ) {
78
- ++$ expect ;
79
- }
80
- }
81
- }
41
+ $ codes = array (
42
+ "\n" => 'onKeyEnter ' ,
43
+ "\x7f" => 'onKeyBackspace ' ,
44
+ "\t" => 'onKeyTab ' ,
45
+
46
+ "\033[A " => 'onKeyUp ' ,
47
+ "\033[B " => 'onKeyDown ' ,
48
+ "\033[C " => 'onKeyRight ' ,
49
+ "\033[D " => 'onKeyLeft ' ,
50
+
51
+ "\033[1~ " => 'onKeyHome ' ,
52
+ "\033[2~ " => 'onKeyInsert ' ,
53
+ "\033[3~ " => 'onKeyDelete ' ,
54
+ "\033[4~ " => 'onKeyEnd ' ,
55
+
56
+ // "\033[20~" => 'onKeyF10',
57
+ );
58
+ $ decode = function ($ code ) use ($ codes , $ that ) {
59
+ if (isset ($ codes [$ code ])) {
60
+ $ method = $ codes [$ code ];
61
+ $ that ->$ method ($ code );
62
+ return ;
82
63
}
83
- $ char .= $ byte ;
84
- --$ expect ;
85
-
86
- // forward buffered bytes as a single multi byte character once last byte has been read
87
- if ($ expect === 0 ) {
88
- $ save = $ char ;
89
- $ char = '' ;
90
- $ that ->onFallback ($ save );
91
- }
92
- });
64
+ };
65
+
66
+ $ parser ->on ('csi ' , $ decode );
67
+ $ parser ->on ('c0 ' , $ decode );
93
68
94
- $ this -> sequencer -> addFallback ( self :: ESC_SEQUENCE , function ( $ bytes ) {
95
- echo ' unknown sequence: ' . ord ( $ bytes ) . PHP_EOL ;
96
- } );
69
+ // push resulting data through utf8 sequencer
70
+ $ utf8 = new Utf8Sequencer ( $ parser ) ;
71
+ $ utf8 -> on ( ' data ' , array ( $ this , ' onFallback ' ) );
97
72
98
- // input data emits a single char into readline
99
- $ input ->on ('data ' , array ($ this ->sequencer , 'push ' ));
100
- $ input ->on ('end ' , array ($ this , 'handleEnd ' ));
101
- $ input ->on ('error ' , array ($ this , 'handleError ' ));
102
- $ input ->on ('close ' , array ($ this , 'close ' ));
73
+ // process all stream events (forwarded from input stream)
74
+ $ utf8 ->on ('end ' , array ($ this , 'handleEnd ' ));
75
+ $ utf8 ->on ('error ' , array ($ this , 'handleError ' ));
76
+ $ utf8 ->on ('close ' , array ($ this , 'close ' ));
103
77
}
104
78
105
79
/**
@@ -519,7 +493,7 @@ public function onFallback($chars)
519
493
$ post = $ this ->substr ($ this ->linebuffer , $ this ->linepos );
520
494
521
495
$ this ->linebuffer = $ pre . $ chars . $ post ;
522
- ++ $ this ->linepos ;
496
+ $ this ->linepos += $ this -> strlen ( $ chars ) ;
523
497
524
498
$ this ->redraw ();
525
499
}
0 commit comments