1212namespace Symfony \Component \Console \Helper ;
1313
1414use Symfony \Component \Console \Output \OutputInterface ;
15+ use Symfony \Component \Console \Output \StreamOutput ;
1516
1617/**
1718 * The Dialog class provides helpers to interact with the user.
@@ -31,15 +32,102 @@ class DialogHelper extends Helper
3132 *
3233 * @return string The user answer
3334 */
34- public function ask (OutputInterface $ output , $ question , $ default = null )
35+ public function ask (OutputInterface $ output , $ question , $ default = null , $ autocomplete = null )
3536 {
3637 $ output ->write ($ question );
3738
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 " );
41130 }
42- $ ret = trim ($ ret );
43131
44132 return strlen ($ ret ) > 0 ? $ ret : $ default ;
45133 }
@@ -78,23 +166,24 @@ public function askConfirmation(OutputInterface $output, $question, $default = t
78166 *
79167 * @param OutputInterface $output
80168 * @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
84173 *
85174 * @return mixed
86175 *
87176 * @throws \Exception When any of the validator returns an error
88177 */
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 )
90179 {
91180 $ error = null ;
92181 while (false === $ attempts || $ attempts --) {
93182 if (null !== $ error ) {
94183 $ output ->writeln ($ this ->getHelperSet ()->get ('formatter ' )->formatBlock ($ error ->getMessage (), 'error ' ));
95184 }
96185
97- $ value = $ this ->ask ($ output , $ question , $ default );
186+ $ value = $ this ->ask ($ output , $ question , $ default, $ autocomplete );
98187
99188 try {
100189 return call_user_func ($ validator , $ value );
0 commit comments