Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / inquirer / lib / prompts / list.js
1 'use strict';
2 /**
3  * `list` type prompt
4  */
5
6 var _ = {
7   isNumber: require('lodash/isNumber'),
8   findIndex: require('lodash/findIndex'),
9   isString: require('lodash/isString'),
10 };
11 var chalk = require('chalk');
12 var figures = require('figures');
13 var cliCursor = require('cli-cursor');
14 var runAsync = require('run-async');
15 var { flatMap, map, take, takeUntil } = require('rxjs/operators');
16 var Base = require('./base');
17 var observe = require('../utils/events');
18 var Paginator = require('../utils/paginator');
19 var incrementListIndex = require('../utils/incrementListIndex');
20
21 class ListPrompt extends Base {
22   constructor(questions, rl, answers) {
23     super(questions, rl, answers);
24
25     if (!this.opt.choices) {
26       this.throwParamError('choices');
27     }
28
29     this.firstRender = true;
30     this.selected = 0;
31
32     var def = this.opt.default;
33
34     // If def is a Number, then use as index. Otherwise, check for value.
35     if (_.isNumber(def) && def >= 0 && def < this.opt.choices.realLength) {
36       this.selected = def;
37     } else if (!_.isNumber(def) && def != null) {
38       let index = _.findIndex(this.opt.choices.realChoices, ({ value }) => value === def);
39       this.selected = Math.max(index, 0);
40     }
41
42     // Make sure no default is set (so it won't be printed)
43     this.opt.default = null;
44
45     const shouldLoop = this.opt.loop === undefined ? true : this.opt.loop;
46     this.paginator = new Paginator(this.screen, { isInfinite: shouldLoop });
47   }
48
49   /**
50    * Start the Inquiry session
51    * @param  {Function} cb      Callback when prompt is done
52    * @return {this}
53    */
54
55   _run(cb) {
56     this.done = cb;
57
58     var self = this;
59
60     var events = observe(this.rl);
61     events.normalizedUpKey.pipe(takeUntil(events.line)).forEach(this.onUpKey.bind(this));
62     events.normalizedDownKey
63       .pipe(takeUntil(events.line))
64       .forEach(this.onDownKey.bind(this));
65     events.numberKey.pipe(takeUntil(events.line)).forEach(this.onNumberKey.bind(this));
66     events.line
67       .pipe(
68         take(1),
69         map(this.getCurrentValue.bind(this)),
70         flatMap((value) => runAsync(self.opt.filter)(value).catch((err) => err))
71       )
72       .forEach(this.onSubmit.bind(this));
73
74     // Init the prompt
75     cliCursor.hide();
76     this.render();
77
78     return this;
79   }
80
81   /**
82    * Render the prompt to screen
83    * @return {ListPrompt} self
84    */
85
86   render() {
87     // Render question
88     var message = this.getQuestion();
89
90     if (this.firstRender) {
91       message += chalk.dim('(Use arrow keys)');
92     }
93
94     // Render choices or answer depending on the state
95     if (this.status === 'answered') {
96       message += chalk.cyan(this.opt.choices.getChoice(this.selected).short);
97     } else {
98       var choicesStr = listRender(this.opt.choices, this.selected);
99       var indexPosition = this.opt.choices.indexOf(
100         this.opt.choices.getChoice(this.selected)
101       );
102       var realIndexPosition =
103         this.opt.choices.reduce(function (acc, value, i) {
104           // Dont count lines past the choice we are looking at
105           if (i > indexPosition) {
106             return acc;
107           }
108           // Add line if it's a separator
109           if (value.type === 'separator') {
110             return acc + 1;
111           }
112
113           var l = value.name;
114           // Non-strings take up one line
115           if (typeof l !== 'string') {
116             return acc + 1;
117           }
118
119           // Calculate lines taken up by string
120           l = l.split('\n');
121           return acc + l.length;
122         }, 0) - 1;
123       message +=
124         '\n' + this.paginator.paginate(choicesStr, realIndexPosition, this.opt.pageSize);
125     }
126
127     this.firstRender = false;
128
129     this.screen.render(message);
130   }
131
132   /**
133    * When user press `enter` key
134    */
135
136   onSubmit(value) {
137     this.status = 'answered';
138
139     // Rerender prompt
140     this.render();
141
142     this.screen.done();
143     cliCursor.show();
144     this.done(value);
145   }
146
147   getCurrentValue() {
148     return this.opt.choices.getChoice(this.selected).value;
149   }
150
151   /**
152    * When user press a key
153    */
154   onUpKey() {
155     this.selected = incrementListIndex(this.selected, 'up', this.opt);
156     this.render();
157   }
158
159   onDownKey() {
160     this.selected = incrementListIndex(this.selected, 'down', this.opt);
161     this.render();
162   }
163
164   onNumberKey(input) {
165     if (input <= this.opt.choices.realLength) {
166       this.selected = input - 1;
167     }
168
169     this.render();
170   }
171 }
172
173 /**
174  * Function for rendering list choices
175  * @param  {Number} pointer Position of the pointer
176  * @return {String}         Rendered content
177  */
178 function listRender(choices, pointer) {
179   var output = '';
180   var separatorOffset = 0;
181
182   choices.forEach((choice, i) => {
183     if (choice.type === 'separator') {
184       separatorOffset++;
185       output += '  ' + choice + '\n';
186       return;
187     }
188
189     if (choice.disabled) {
190       separatorOffset++;
191       output += '  - ' + choice.name;
192       output += ' (' + (_.isString(choice.disabled) ? choice.disabled : 'Disabled') + ')';
193       output += '\n';
194       return;
195     }
196
197     var isSelected = i - separatorOffset === pointer;
198     var line = (isSelected ? figures.pointer + ' ' : '  ') + choice.name;
199     if (isSelected) {
200       line = chalk.cyan(line);
201     }
202
203     output += line + ' \n';
204   });
205
206   return output.replace(/\n$/, '');
207 }
208
209 module.exports = ListPrompt;