@@ -9,7 +9,7 @@ use crate::{
9
9
validate:: Validator ,
10
10
} ;
11
11
12
- use console:: Term ;
12
+ use console:: { Key , Term } ;
13
13
14
14
/// Renders an input prompt.
15
15
///
@@ -142,6 +142,104 @@ where
142
142
self
143
143
}
144
144
145
+ /// Enables the user to enter a printable ascii sequence and returns the result.
146
+ ///
147
+ /// The dialog is rendered on stderr.
148
+ pub fn interact_text ( & self ) -> io:: Result < T > {
149
+ self . interact_text_on ( & Term :: stderr ( ) )
150
+ }
151
+
152
+ /// Like `interact_text` but allows a specific terminal to be set.
153
+ pub fn interact_text_on ( & self , term : & Term ) -> io:: Result < T > {
154
+ let mut render = TermThemeRenderer :: new ( term, self . theme ) ;
155
+
156
+ loop {
157
+ let default_string = self . default . as_ref ( ) . map ( |x| x. to_string ( ) ) ;
158
+
159
+ render. input_prompt (
160
+ & self . prompt ,
161
+ if self . show_default {
162
+ default_string. as_ref ( ) . map ( |x| x. as_str ( ) )
163
+ } else {
164
+ None
165
+ } ,
166
+ ) ?;
167
+ term. flush ( ) ?;
168
+
169
+ // Read input by keystroke so that we can suppress ascii control characters
170
+ if !term. is_term ( ) {
171
+ return Ok ( "" . to_owned ( ) . parse :: < T > ( ) . unwrap ( ) ) ;
172
+ }
173
+
174
+ let mut chars: Vec < char > = Vec :: new ( ) ;
175
+ if let Some ( initial) = self . initial_text . as_ref ( ) {
176
+ term. write_str ( initial) ?;
177
+ chars = initial. chars ( ) . collect ( ) ;
178
+ }
179
+ loop {
180
+ match term. read_key ( ) ? {
181
+ Key :: Backspace => {
182
+ if chars. pop ( ) . is_some ( ) {
183
+ term. clear_chars ( 1 ) ?;
184
+ }
185
+ term. flush ( ) ?;
186
+ }
187
+ Key :: Char ( chr) => {
188
+ if !chr. is_ascii_control ( ) {
189
+ chars. push ( chr) ;
190
+ let mut bytes_char = [ 0 ; 4 ] ;
191
+ chr. encode_utf8 ( & mut bytes_char) ;
192
+ term. write_str ( chr. encode_utf8 ( & mut bytes_char) ) ?;
193
+ term. flush ( ) ?;
194
+ }
195
+ }
196
+ Key :: Enter => break ,
197
+ Key :: Unknown => {
198
+ return Err ( io:: Error :: new (
199
+ io:: ErrorKind :: NotConnected ,
200
+ "Not a terminal" ,
201
+ ) )
202
+ }
203
+ _ => ( ) ,
204
+ }
205
+ }
206
+ let input = chars. iter ( ) . collect :: < String > ( ) ;
207
+
208
+ term. clear_line ( ) ?;
209
+ render. clear ( ) ?;
210
+
211
+ if chars. is_empty ( ) {
212
+ if let Some ( ref default) = self . default {
213
+ render. input_prompt_selection ( & self . prompt , & default. to_string ( ) ) ?;
214
+ term. flush ( ) ?;
215
+ return Ok ( default. clone ( ) ) ;
216
+ } else if !self . permit_empty {
217
+ continue ;
218
+ }
219
+ }
220
+
221
+ match input. parse :: < T > ( ) {
222
+ Ok ( value) => {
223
+ if let Some ( ref validator) = self . validator {
224
+ if let Some ( err) = validator ( & input) {
225
+ render. error ( & err) ?;
226
+ continue ;
227
+ }
228
+ }
229
+
230
+ render. input_prompt_selection ( & self . prompt , & input) ?;
231
+ term. flush ( ) ?;
232
+
233
+ return Ok ( value) ;
234
+ }
235
+ Err ( err) => {
236
+ render. error ( & err. to_string ( ) ) ?;
237
+ continue ;
238
+ }
239
+ }
240
+ }
241
+ }
242
+
145
243
/// Enables user interaction and returns the result.
146
244
///
147
245
/// If the user confirms the result is `true`, `false` otherwise.
0 commit comments