11'use strict' ;
22
3- const util = require ( '../util' ) ;
43const color = require ( 'kleur' ) ;
54const Prompt = require ( './prompt' ) ;
65const { cursor } = require ( 'sisteransi' ) ;
6+ const { style, clear, figures, strip } = require ( '../util' ) ;
77
8- // Get value, with fallback to title
98const getVal = ( arr , i ) => arr [ i ] && ( arr [ i ] . value || arr [ i ] . title || arr [ i ] ) ;
9+ const getTitle = ( arr , i ) => arr [ i ] && ( arr [ i ] . title || arr [ i ] . value || arr [ i ] ) ;
1010
1111/**
1212 * TextPrompt Base Element
@@ -19,6 +19,8 @@ const getVal = (arr, i) => arr[i] && (arr[i].value || arr[i].title || arr[i]);
1919 * @param {String } [opts.style='default'] Render style
2020 * @param {String } [opts.fallback] Fallback message - initial to default value
2121 * @param {String } [opts.initial] Index of the default value
22+ * @param {Stream } [opts.stdin] The Readable stream to listen to
23+ * @param {Stream } [opts.stdout] The Writable stream to write readline data to
2224 */
2325class AutocompletePrompt extends Prompt {
2426 constructor ( opts = { } ) {
@@ -27,21 +29,25 @@ class AutocompletePrompt extends Prompt {
2729 this . suggest = opts . suggest ;
2830 this . choices = opts . choices ;
2931 this . initial = opts . initial ;
30- this . cursor = opts . initial || opts . cursor || 0 ;
31- this . fallback = opts . fallback || opts . initial !== void 0 ? `${ util . figures . pointerSmall } ${ getVal ( this . choices , this . initial ) } ` : `${ util . figures . pointerSmall } no matches found` ;
32+ this . select = opts . initial || opts . cursor || 0 ;
33+ this . fallback = opts . fallback || opts . initial !== void 0 ?
34+ `${ figures . pointerSmall } ${ getTitle ( this . choices , this . initial ) } ` :
35+ `${ figures . pointerSmall } no matches found` ;
3236 this . suggestions = [ ] ;
3337 this . input = '' ;
3438 this . limit = opts . limit || 10 ;
35- this . transform = util . style . render ( opts . style ) ;
39+ this . cursor = 0 ;
40+ this . transform = style . render ( opts . style ) ;
41+ this . scale = this . transform . scale ;
3642 this . render = this . render . bind ( this ) ;
3743 this . complete = this . complete . bind ( this ) ;
38- this . clear = util . clear ( '' ) ;
44+ this . clear = clear ( '' ) ;
3945 this . complete ( this . render ) ;
4046 this . render ( true ) ;
4147 }
4248
43- moveCursor ( i ) {
44- this . cursor = i ;
49+ moveSelect ( i ) {
50+ this . select = i ;
4551 if ( this . suggestions . length > 0 ) this . value = getVal ( this . suggestions , i ) ;
4652 else this . value = this . initial !== void 0 ? getVal ( this . choices , this . initial ) : null ;
4753 this . fire ( ) ;
@@ -53,19 +59,19 @@ class AutocompletePrompt extends Prompt {
5359
5460 if ( this . completing !== p ) return ;
5561
56- this . suggestions = suggestions . slice ( 0 , this . limit ) . map ( s => util . strip ( s ) ) ;
62+ this . suggestions = suggestions . slice ( 0 , this . limit ) . map ( s => strip ( s ) ) ;
5763 this . completing = false ;
5864
5965 const l = Math . max ( suggestions . length - 1 , 0 ) ;
60- this . moveCursor ( Math . min ( l , this . cursor ) ) ;
66+ this . moveSelect ( Math . min ( l , this . select ) ) ;
6167
6268 cb && cb ( ) ;
6369 }
6470
6571 reset ( ) {
6672 this . input = '' ;
6773 this . complete ( ( ) => {
68- this . moveCursor ( this . initial !== void 0 ? this . initial : 0 ) ;
74+ this . moveSelect ( this . initial !== void 0 ? this . initial : 0 ) ;
6975 this . render ( ) ;
7076 } ) ;
7177 this . render ( ) ;
@@ -89,68 +95,98 @@ class AutocompletePrompt extends Prompt {
8995 }
9096
9197 _ ( c , key ) {
92- this . input += c ;
98+ let s1 = this . input . slice ( 0 , this . cursor ) ;
99+ let s2 = this . input . slice ( this . cursor ) ;
100+ this . cursor = this . cursor + 1 ;
101+ this . input = `${ s1 } ${ c } ${ s2 } ` ;
93102 this . complete ( this . render ) ;
94103 this . render ( ) ;
95104 }
96105
97106 delete ( ) {
98107 if ( this . input . length === 0 ) return this . bell ( ) ;
99- this . input = this . input . slice ( 0 , - 1 ) ;
108+ let s1 = this . input . slice ( 0 , this . cursor - 1 ) ;
109+ let s2 = this . input . slice ( this . cursor ) ;
110+ this . input = `${ s1 } ${ s2 } ` ;
100111 this . complete ( this . render ) ;
112+ this . cursor = this . cursor - 1 ;
101113 this . render ( ) ;
102114 }
103115
104116 first ( ) {
105- this . moveCursor ( 0 ) ;
117+ this . moveSelect ( 0 ) ;
106118 this . render ( ) ;
107119 }
108120
109121 last ( ) {
110- this . moveCursor ( this . suggestions . length - 1 ) ;
122+ this . moveSelect ( this . suggestions . length - 1 ) ;
111123 this . render ( ) ;
112124 }
113125
114126 up ( ) {
115- if ( this . cursor <= 0 ) return this . bell ( ) ;
116- this . moveCursor ( this . cursor - 1 ) ;
127+ if ( this . select <= 0 ) return this . bell ( ) ;
128+ this . moveSelect ( this . select - 1 ) ;
117129 this . render ( ) ;
118130 }
119131
120132 down ( ) {
121- if ( this . cursor >= this . suggestions . length - 1 ) return this . bell ( ) ;
122- this . moveCursor ( this . cursor + 1 ) ;
133+ if ( this . select >= this . suggestions . length - 1 ) return this . bell ( ) ;
134+ this . moveSelect ( this . select + 1 ) ;
123135 this . render ( ) ;
124136 }
125137
126138 next ( ) {
127- this . moveCursor ( ( this . cursor + 1 ) % this . suggestions . length ) ;
139+ this . moveSelect ( ( this . select + 1 ) % this . suggestions . length ) ;
128140 this . render ( ) ;
129141 }
130142
131- render ( first ) {
132- if ( first ) this . out . write ( cursor . hide ) ;
143+ left ( ) {
144+ if ( this . cursor <= 0 ) return this . bell ( ) ;
145+ this . cursor = this . cursor - 1 ;
146+ this . render ( ) ;
147+ }
133148
134- let prompt = [
135- util . style . symbol ( this . done , this . aborted ) ,
136- this . msg ,
137- util . style . delimiter ( this . completing ) ,
138- this . done && this . suggestions [ this . cursor ]
139- ? this . suggestions [ this . cursor ] . title
140- : this . transform . render ( this . input )
141- ] . join ( ' ' ) ;
149+ right ( ) {
150+ if ( this . cursor * this . scale >= this . rendered . length ) return this . bell ( ) ;
151+ this . cursor = this . cursor + 1 ;
152+ this . render ( ) ;
153+ }
142154
143- if ( ! this . done ) {
144- let suggestions = this . suggestions . map ( ( item , i ) =>
145- `\n${ i === this . cursor ? color . cyan ( item . title ) : item . title } ` ) ;
155+ render ( ) {
156+ if ( this . lineCount ) this . out . write ( cursor . down ( this . lineCount ) ) ;
146157
147- prompt += suggestions . length ?
148- suggestions . reduce ( ( acc , line ) => acc + line , '' ) :
149- `\n${ color . gray ( this . fallback ) } ` ;
158+ let prompt = `${ style . symbol ( this . done , this . aborted ) } ${ this . msg } ${ style . delimiter ( this . completing ) } ` ;
159+ let length = strip ( prompt ) . length ;
160+
161+ if ( this . done && this . suggestions [ this . select ] ) {
162+ prompt += `${ this . suggestions [ this . select ] . title } ` ;
163+ } else {
164+ this . rendered = `${ this . transform . render ( this . input ) } `
165+ length += this . rendered . length ;
166+ prompt += this . rendered ;
167+ }
168+
169+ if ( ! this . done ) {
170+ this . lineCount = this . suggestions . length ;
171+ let suggestions = this . suggestions . reduce ( ( acc , item , i ) =>
172+ acc += `\n${ i === this . select ? color . cyan ( item . title ) : item . title } ` , '' ) ;
173+ if ( suggestions ) {
174+ prompt += suggestions ;
175+ } else {
176+ prompt += `\n${ color . gray ( this . fallback ) } ` ;
177+ this . lineCount += 1 ;
178+ }
150179 }
151180
152181 this . out . write ( this . clear + prompt ) ;
153- this . clear = util . clear ( prompt ) ;
182+ this . clear = clear ( prompt ) ;
183+
184+ if ( this . lineCount && ! this . done ) {
185+ let pos = cursor . up ( this . lineCount ) ;
186+ pos += cursor . left + cursor . to ( length ) ;
187+ pos += cursor . move ( - this . rendered . length + this . cursor * this . scale ) ;
188+ this . out . write ( pos ) ;
189+ }
154190 }
155191}
156192
0 commit comments