12
12
namespace Symfony \Component \Console \Helper ;
13
13
14
14
use Symfony \Component \Console \Output \OutputInterface ;
15
+ use Symfony \Component \Console \Output \StreamOutput ;
15
16
16
17
/**
17
18
* The Dialog class provides helpers to interact with the user.
@@ -31,15 +32,102 @@ class DialogHelper extends Helper
31
32
*
32
33
* @return string The user answer
33
34
*/
34
- public function ask (OutputInterface $ output , $ question , $ default = null )
35
+ public function ask (OutputInterface $ output , $ question , $ default = null , $ autocomplete = null )
35
36
{
36
37
$ output ->write ($ question );
37
38
38
- $ ret = fgets ($ this ->inputStream ?: STDIN , 4096 );
39
- if (false === $ ret ) {
40
- throw new \RuntimeException ('Aborted ' );
39
+ $ inputStream = (null === $ this ->inputStream ? STDIN : $ this ->inputStream );
40
+
41
+ if (null === $ autocomplete || !($ output instanceof StreamOutput && $ output ->hasColorSupport ())) {
42
+ $ ret = fgets ($ inputStream );
43
+ if (false === $ ret ) {
44
+ throw new \RuntimeException ('Aborted ' );
45
+ }
46
+ $ ret = trim ($ ret );
47
+ } else {
48
+ $ i = 0 ;
49
+ $ currentMatched = false ;
50
+ $ ret = '' ;
51
+
52
+ // Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead)
53
+ system ("stty -icanon -echo " );
54
+
55
+ while ($ c = fread ($ inputStream , 3 )) {
56
+ // Did we read an escape character?
57
+ if (strlen ($ c ) > 1 && $ c [0 ] == "\033" ) {
58
+ // Escape sequences for arrow keys
59
+ if ($ c [2 ] == 'A ' || $ c [2 ] == 'B ' || $ c [2 ] == 'C ' || $ c [2 ] == 'D ' ) {
60
+ continue ;
61
+ }
62
+ }
63
+
64
+ // Backspace Character
65
+ if (ord ($ c ) == 127 ) {
66
+ if ($ i == 0 ) {
67
+ continue ;
68
+ }
69
+
70
+ // Move cursor backwards
71
+ $ output ->write ("\033[1D " );
72
+ // Erase characters from cursor to end of line
73
+ $ output ->write ("\033[K " );
74
+ $ ret = substr ($ ret , 0 , --$ i );
75
+
76
+ continue ;
77
+ }
78
+
79
+ if ($ c == "\t" | $ c == "\n" ) {
80
+ if (false !== $ currentMatched ) {
81
+ // Echo out completed match
82
+ $ output ->write (substr ($ autocomplete [$ currentMatched ], strlen ($ ret )));
83
+ $ ret = $ autocomplete [$ currentMatched ];
84
+ $ i = strlen ($ ret );
85
+ }
86
+
87
+ if ($ c == "\n" ) {
88
+ $ output ->write ($ c );
89
+ break ;
90
+ }
91
+
92
+ continue ;
93
+ }
94
+
95
+ $ output ->write ($ c );
96
+ $ ret .= $ c ;
97
+ $ i ++;
98
+
99
+ $ lastMatch = $ currentMatched ;
100
+
101
+ // Erase characters from cursor to end of line
102
+ $ output ->write ("\033[K " );
103
+
104
+ for ($ j = 0 ; $ j < count ($ autocomplete ); $ j ++) {
105
+ $ matchTest = substr ($ autocomplete [$ j ], 0 , strlen ($ ret ));
106
+
107
+ if ($ ret == $ matchTest ) {
108
+ // Save cursor position
109
+ $ output ->write ("\0337 " );
110
+
111
+ // Set fore/background colour to make text appear highlighted
112
+ $ output ->write ("\033[47;30m " );
113
+ $ output ->write (substr ($ autocomplete [$ j ], strlen ($ ret )));
114
+ // Reset text colour
115
+ $ output ->write ("\033[0m " );
116
+
117
+ // Restore cursor position
118
+ $ output ->write ("\0338 " );
119
+
120
+ $ currentMatched = $ j ;
121
+ break ;
122
+ }
123
+
124
+ $ currentMatched = false ;
125
+ }
126
+ }
127
+
128
+ // Reset stty so it behaves normally again
129
+ system ("stty icanon echo " );
41
130
}
42
- $ ret = trim ($ ret );
43
131
44
132
return strlen ($ ret ) > 0 ? $ ret : $ default ;
45
133
}
@@ -78,23 +166,24 @@ public function askConfirmation(OutputInterface $output, $question, $default = t
78
166
*
79
167
* @param OutputInterface $output
80
168
* @param string|array $question
81
- * @param callback $validator A PHP callback
82
- * @param integer $attempts Max number of times to ask before giving up (false by default, which means infinite)
83
- * @param string $default The default answer if none is given by the user
169
+ * @param callback $validator A PHP callback
170
+ * @param integer $attempts Max number of times to ask before giving up (false by default, which means infinite)
171
+ * @param string $default The default answer if none is given by the user
172
+ * @param array $autoComplete
84
173
*
85
174
* @return mixed
86
175
*
87
176
* @throws \Exception When any of the validator returns an error
88
177
*/
89
- public function askAndValidate (OutputInterface $ output , $ question , $ validator , $ attempts = false , $ default = null )
178
+ public function askAndValidate (OutputInterface $ output , $ question , $ validator , $ attempts = false , $ default = null , $ autocomplete = null )
90
179
{
91
180
$ error = null ;
92
181
while (false === $ attempts || $ attempts --) {
93
182
if (null !== $ error ) {
94
183
$ output ->writeln ($ this ->getHelperSet ()->get ('formatter ' )->formatBlock ($ error ->getMessage (), 'error ' ));
95
184
}
96
185
97
- $ value = $ this ->ask ($ output , $ question , $ default );
186
+ $ value = $ this ->ask ($ output , $ question , $ default, $ autocomplete );
98
187
99
188
try {
100
189
return call_user_func ($ validator , $ value );
0 commit comments