.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / enquirer / lib / types / array.js
1 'use strict';
2
3 const colors = require('ansi-colors');
4 const Prompt = require('../prompt');
5 const roles = require('../roles');
6 const utils = require('../utils');
7 const { reorder, scrollUp, scrollDown, isObject, swap } = utils;
8
9 class ArrayPrompt extends Prompt {
10   constructor(options) {
11     super(options);
12     this.cursorHide();
13     this.maxSelected = options.maxSelected || Infinity;
14     this.multiple = options.multiple || false;
15     this.initial = options.initial || 0;
16     this.delay = options.delay || 0;
17     this.longest = 0;
18     this.num = '';
19   }
20
21   async initialize() {
22     if (typeof this.options.initial === 'function') {
23       this.initial = await this.options.initial.call(this);
24     }
25     await this.reset(true);
26     await super.initialize();
27   }
28
29   async reset() {
30     let { choices, initial, autofocus, suggest } = this.options;
31     this.state._choices = [];
32     this.state.choices = [];
33
34     this.choices = await Promise.all(await this.toChoices(choices));
35     this.choices.forEach(ch => (ch.enabled = false));
36
37     if (typeof suggest !== 'function' && this.selectable.length === 0) {
38       throw new Error('At least one choice must be selectable');
39     }
40
41     if (isObject(initial)) initial = Object.keys(initial);
42     if (Array.isArray(initial)) {
43       if (autofocus != null) this.index = this.findIndex(autofocus);
44       initial.forEach(v => this.enable(this.find(v)));
45       await this.render();
46     } else {
47       if (autofocus != null) initial = autofocus;
48       if (typeof initial === 'string') initial = this.findIndex(initial);
49       if (typeof initial === 'number' && initial > -1) {
50         this.index = Math.max(0, Math.min(initial, this.choices.length));
51         this.enable(this.find(this.index));
52       }
53     }
54
55     if (this.isDisabled(this.focused)) {
56       await this.down();
57     }
58   }
59
60   async toChoices(value, parent) {
61     this.state.loadingChoices = true;
62     let choices = [];
63     let index = 0;
64
65     let toChoices = async(items, parent) => {
66       if (typeof items === 'function') items = await items.call(this);
67       if (items instanceof Promise) items = await items;
68
69       for (let i = 0; i < items.length; i++) {
70         let choice = items[i] = await this.toChoice(items[i], index++, parent);
71         choices.push(choice);
72
73         if (choice.choices) {
74           await toChoices(choice.choices, choice);
75         }
76       }
77       return choices;
78     };
79
80     return toChoices(value, parent)
81       .then(choices => {
82         this.state.loadingChoices = false;
83         return choices;
84       });
85   }
86
87   async toChoice(ele, i, parent) {
88     if (typeof ele === 'function') ele = await ele.call(this, this);
89     if (ele instanceof Promise) ele = await ele;
90     if (typeof ele === 'string') ele = { name: ele };
91
92     if (ele.normalized) return ele;
93     ele.normalized = true;
94
95     let origVal = ele.value;
96     let role = roles(ele.role, this.options);
97     ele = role(this, ele);
98
99     if (typeof ele.disabled === 'string' && !ele.hint) {
100       ele.hint = ele.disabled;
101       ele.disabled = true;
102     }
103
104     if (ele.disabled === true && ele.hint == null) {
105       ele.hint = '(disabled)';
106     }
107
108     // if the choice was already normalized, return it
109     if (ele.index != null) return ele;
110     ele.name = ele.name || ele.key || ele.title || ele.value || ele.message;
111     ele.message = ele.message || ele.name || '';
112     ele.value = [ele.value, ele.name].find(this.isValue.bind(this));
113
114     ele.input = '';
115     ele.index = i;
116     ele.cursor = 0;
117
118     utils.define(ele, 'parent', parent);
119     ele.level = parent ? parent.level + 1 : 1;
120     if (ele.indent == null) {
121       ele.indent = parent ? parent.indent + '  ' : (ele.indent || '');
122     }
123
124     ele.path = parent ? parent.path + '.' + ele.name : ele.name;
125     ele.enabled = !!(this.multiple && !this.isDisabled(ele) && (ele.enabled || this.isSelected(ele)));
126
127     if (!this.isDisabled(ele)) {
128       this.longest = Math.max(this.longest, colors.unstyle(ele.message).length);
129     }
130
131     // shallow clone the choice first
132     let choice = { ...ele };
133
134     // then allow the choice to be reset using the "original" values
135     ele.reset = (input = choice.input, value = choice.value) => {
136       for (let key of Object.keys(choice)) ele[key] = choice[key];
137       ele.input = input;
138       ele.value = value;
139     };
140
141     if (origVal == null && typeof ele.initial === 'function') {
142       ele.input = await ele.initial.call(this, this.state, ele, i);
143     }
144
145     return ele;
146   }
147
148   async onChoice(choice, i) {
149     this.emit('choice', choice, i, this);
150
151     if (typeof choice.onChoice === 'function') {
152       await choice.onChoice.call(this, this.state, choice, i);
153     }
154   }
155
156   async addChoice(ele, i, parent) {
157     let choice = await this.toChoice(ele, i, parent);
158     this.choices.push(choice);
159     this.index = this.choices.length - 1;
160     this.limit = this.choices.length;
161     return choice;
162   }
163
164   async newItem(item, i, parent) {
165     let ele = { name: 'New choice name?', editable: true, newChoice: true, ...item };
166     let choice = await this.addChoice(ele, i, parent);
167
168     choice.updateChoice = () => {
169       delete choice.newChoice;
170       choice.name = choice.message = choice.input;
171       choice.input = '';
172       choice.cursor = 0;
173     };
174
175     return this.render();
176   }
177
178   indent(choice) {
179     if (choice.indent == null) {
180       return choice.level > 1 ? '  '.repeat(choice.level - 1) : '';
181     }
182     return choice.indent;
183   }
184
185   dispatch(s, key) {
186     if (this.multiple && this[key.name]) return this[key.name]();
187     this.alert();
188   }
189
190   focus(choice, enabled) {
191     if (typeof enabled !== 'boolean') enabled = choice.enabled;
192     if (enabled && !choice.enabled && this.selected.length >= this.maxSelected) {
193       return this.alert();
194     }
195     this.index = choice.index;
196     choice.enabled = enabled && !this.isDisabled(choice);
197     return choice;
198   }
199
200   space() {
201     if (!this.multiple) return this.alert();
202     this.toggle(this.focused);
203     return this.render();
204   }
205
206   a() {
207     if (this.maxSelected < this.choices.length) return this.alert();
208     let enabled = this.selectable.every(ch => ch.enabled);
209     this.choices.forEach(ch => (ch.enabled = !enabled));
210     return this.render();
211   }
212
213   i() {
214     // don't allow choices to be inverted if it will result in
215     // more than the maximum number of allowed selected items.
216     if (this.choices.length - this.selected.length > this.maxSelected) {
217       return this.alert();
218     }
219     this.choices.forEach(ch => (ch.enabled = !ch.enabled));
220     return this.render();
221   }
222
223   g(choice = this.focused) {
224     if (!this.choices.some(ch => !!ch.parent)) return this.a();
225     this.toggle((choice.parent && !choice.choices) ? choice.parent : choice);
226     return this.render();
227   }
228
229   toggle(choice, enabled) {
230     if (!choice.enabled && this.selected.length >= this.maxSelected) {
231       return this.alert();
232     }
233
234     if (typeof enabled !== 'boolean') enabled = !choice.enabled;
235     choice.enabled = enabled;
236
237     if (choice.choices) {
238       choice.choices.forEach(ch => this.toggle(ch, enabled));
239     }
240
241     let parent = choice.parent;
242     while (parent) {
243       let choices = parent.choices.filter(ch => this.isDisabled(ch));
244       parent.enabled = choices.every(ch => ch.enabled === true);
245       parent = parent.parent;
246     }
247
248     reset(this, this.choices);
249     this.emit('toggle', choice, this);
250     return choice;
251   }
252
253   enable(choice) {
254     if (this.selected.length >= this.maxSelected) return this.alert();
255     choice.enabled = !this.isDisabled(choice);
256     choice.choices && choice.choices.forEach(this.enable.bind(this));
257     return choice;
258   }
259
260   disable(choice) {
261     choice.enabled = false;
262     choice.choices && choice.choices.forEach(this.disable.bind(this));
263     return choice;
264   }
265
266   number(n) {
267     this.num += n;
268
269     let number = num => {
270       let i = Number(num);
271       if (i > this.choices.length - 1) return this.alert();
272
273       let focused = this.focused;
274       let choice = this.choices.find(ch => i === ch.index);
275
276       if (!choice.enabled && this.selected.length >= this.maxSelected) {
277         return this.alert();
278       }
279
280       if (this.visible.indexOf(choice) === -1) {
281         let choices = reorder(this.choices);
282         let actualIdx = choices.indexOf(choice);
283
284         if (focused.index > actualIdx) {
285           let start = choices.slice(actualIdx, actualIdx + this.limit);
286           let end = choices.filter(ch => !start.includes(ch));
287           this.choices = start.concat(end);
288         } else {
289           let pos = actualIdx - this.limit + 1;
290           this.choices = choices.slice(pos).concat(choices.slice(0, pos));
291         }
292       }
293
294       this.index = this.choices.indexOf(choice);
295       this.toggle(this.focused);
296       return this.render();
297     };
298
299     clearTimeout(this.numberTimeout);
300
301     return new Promise(resolve => {
302       let len = this.choices.length;
303       let num = this.num;
304
305       let handle = (val = false, res) => {
306         clearTimeout(this.numberTimeout);
307         if (val) res = number(num);
308         this.num = '';
309         resolve(res);
310       };
311
312       if (num === '0' || (num.length === 1 && Number(num + '0') > len)) {
313         return handle(true);
314       }
315
316       if (Number(num) > len) {
317         return handle(false, this.alert());
318       }
319
320       this.numberTimeout = setTimeout(() => handle(true), this.delay);
321     });
322   }
323
324   home() {
325     this.choices = reorder(this.choices);
326     this.index = 0;
327     return this.render();
328   }
329
330   end() {
331     let pos = this.choices.length - this.limit;
332     let choices = reorder(this.choices);
333     this.choices = choices.slice(pos).concat(choices.slice(0, pos));
334     this.index = this.limit - 1;
335     return this.render();
336   }
337
338   first() {
339     this.index = 0;
340     return this.render();
341   }
342
343   last() {
344     this.index = this.visible.length - 1;
345     return this.render();
346   }
347
348   prev() {
349     if (this.visible.length <= 1) return this.alert();
350     return this.up();
351   }
352
353   next() {
354     if (this.visible.length <= 1) return this.alert();
355     return this.down();
356   }
357
358   right() {
359     if (this.cursor >= this.input.length) return this.alert();
360     this.cursor++;
361     return this.render();
362   }
363
364   left() {
365     if (this.cursor <= 0) return this.alert();
366     this.cursor--;
367     return this.render();
368   }
369
370   up() {
371     let len = this.choices.length;
372     let vis = this.visible.length;
373     let idx = this.index;
374     if (this.options.scroll === false && idx === 0) {
375       return this.alert();
376     }
377     if (len > vis && idx === 0) {
378       return this.scrollUp();
379     }
380     this.index = ((idx - 1 % len) + len) % len;
381     if (this.isDisabled()) {
382       return this.up();
383     }
384     return this.render();
385   }
386
387   down() {
388     let len = this.choices.length;
389     let vis = this.visible.length;
390     let idx = this.index;
391     if (this.options.scroll === false && idx === vis - 1) {
392       return this.alert();
393     }
394     if (len > vis && idx === vis - 1) {
395       return this.scrollDown();
396     }
397     this.index = (idx + 1) % len;
398     if (this.isDisabled()) {
399       return this.down();
400     }
401     return this.render();
402   }
403
404   scrollUp(i = 0) {
405     this.choices = scrollUp(this.choices);
406     this.index = i;
407     if (this.isDisabled()) {
408       return this.up();
409     }
410     return this.render();
411   }
412
413   scrollDown(i = this.visible.length - 1) {
414     this.choices = scrollDown(this.choices);
415     this.index = i;
416     if (this.isDisabled()) {
417       return this.down();
418     }
419     return this.render();
420   }
421
422   async shiftUp() {
423     if (this.options.sort === true) {
424       this.sorting = true;
425       this.swap(this.index - 1);
426       await this.up();
427       this.sorting = false;
428       return;
429     }
430     return this.scrollUp(this.index);
431   }
432
433   async shiftDown() {
434     if (this.options.sort === true) {
435       this.sorting = true;
436       this.swap(this.index + 1);
437       await this.down();
438       this.sorting = false;
439       return;
440     }
441     return this.scrollDown(this.index);
442   }
443
444   pageUp() {
445     if (this.visible.length <= 1) return this.alert();
446     this.limit = Math.max(this.limit - 1, 0);
447     this.index = Math.min(this.limit - 1, this.index);
448     this._limit = this.limit;
449     if (this.isDisabled()) {
450       return this.up();
451     }
452     return this.render();
453   }
454
455   pageDown() {
456     if (this.visible.length >= this.choices.length) return this.alert();
457     this.index = Math.max(0, this.index);
458     this.limit = Math.min(this.limit + 1, this.choices.length);
459     this._limit = this.limit;
460     if (this.isDisabled()) {
461       return this.down();
462     }
463     return this.render();
464   }
465
466   swap(pos) {
467     swap(this.choices, this.index, pos);
468   }
469
470   isDisabled(choice = this.focused) {
471     let keys = ['disabled', 'collapsed', 'hidden', 'completing', 'readonly'];
472     if (choice && keys.some(key => choice[key] === true)) {
473       return true;
474     }
475     return choice && choice.role === 'heading';
476   }
477
478   isEnabled(choice = this.focused) {
479     if (Array.isArray(choice)) return choice.every(ch => this.isEnabled(ch));
480     if (choice.choices) {
481       let choices = choice.choices.filter(ch => !this.isDisabled(ch));
482       return choice.enabled && choices.every(ch => this.isEnabled(ch));
483     }
484     return choice.enabled && !this.isDisabled(choice);
485   }
486
487   isChoice(choice, value) {
488     return choice.name === value || choice.index === Number(value);
489   }
490
491   isSelected(choice) {
492     if (Array.isArray(this.initial)) {
493       return this.initial.some(value => this.isChoice(choice, value));
494     }
495     return this.isChoice(choice, this.initial);
496   }
497
498   map(names = [], prop = 'value') {
499     return [].concat(names || []).reduce((acc, name) => {
500       acc[name] = this.find(name, prop);
501       return acc;
502     }, {});
503   }
504
505   filter(value, prop) {
506     let isChoice = (ele, i) => [ele.name, i].includes(value);
507     let fn = typeof value === 'function' ? value : isChoice;
508     let choices = this.options.multiple ? this.state._choices : this.choices;
509     let result = choices.filter(fn);
510     if (prop) {
511       return result.map(ch => ch[prop]);
512     }
513     return result;
514   }
515
516   find(value, prop) {
517     if (isObject(value)) return prop ? value[prop] : value;
518     let isChoice = (ele, i) => [ele.name, i].includes(value);
519     let fn = typeof value === 'function' ? value : isChoice;
520     let choice = this.choices.find(fn);
521     if (choice) {
522       return prop ? choice[prop] : choice;
523     }
524   }
525
526   findIndex(value) {
527     return this.choices.indexOf(this.find(value));
528   }
529
530   async submit() {
531     let choice = this.focused;
532     if (!choice) return this.alert();
533
534     if (choice.newChoice) {
535       if (!choice.input) return this.alert();
536       choice.updateChoice();
537       return this.render();
538     }
539
540     if (this.choices.some(ch => ch.newChoice)) {
541       return this.alert();
542     }
543
544     let { reorder, sort } = this.options;
545     let multi = this.multiple === true;
546     let value = this.selected;
547     if (value === void 0) {
548       return this.alert();
549     }
550
551     // re-sort choices to original order
552     if (Array.isArray(value) && reorder !== false && sort !== true) {
553       value = utils.reorder(value);
554     }
555
556     this.value = multi ? value.map(ch => ch.name) : value.name;
557     return super.submit();
558   }
559
560   set choices(choices = []) {
561     this.state._choices = this.state._choices || [];
562     this.state.choices = choices;
563
564     for (let choice of choices) {
565       if (!this.state._choices.some(ch => ch.name === choice.name)) {
566         this.state._choices.push(choice);
567       }
568     }
569
570     if (!this._initial && this.options.initial) {
571       this._initial = true;
572       let init = this.initial;
573       if (typeof init === 'string' || typeof init === 'number') {
574         let choice = this.find(init);
575         if (choice) {
576           this.initial = choice.index;
577           this.focus(choice, true);
578         }
579       }
580     }
581   }
582   get choices() {
583     return reset(this, this.state.choices || []);
584   }
585
586   set visible(visible) {
587     this.state.visible = visible;
588   }
589   get visible() {
590     return (this.state.visible || this.choices).slice(0, this.limit);
591   }
592
593   set limit(num) {
594     this.state.limit = num;
595   }
596   get limit() {
597     let { state, options, choices } = this;
598     let limit = state.limit || this._limit || options.limit || choices.length;
599     return Math.min(limit, this.height);
600   }
601
602   set value(value) {
603     super.value = value;
604   }
605   get value() {
606     if (typeof super.value !== 'string' && super.value === this.initial) {
607       return this.input;
608     }
609     return super.value;
610   }
611
612   set index(i) {
613     this.state.index = i;
614   }
615   get index() {
616     return Math.max(0, this.state ? this.state.index : 0);
617   }
618
619   get enabled() {
620     return this.filter(this.isEnabled.bind(this));
621   }
622
623   get focused() {
624     let choice = this.choices[this.index];
625     if (choice && this.state.submitted && this.multiple !== true) {
626       choice.enabled = true;
627     }
628     return choice;
629   }
630
631   get selectable() {
632     return this.choices.filter(choice => !this.isDisabled(choice));
633   }
634
635   get selected() {
636     return this.multiple ? this.enabled : this.focused;
637   }
638 }
639
640 function reset(prompt, choices) {
641   if (choices instanceof Promise) return choices;
642   if (typeof choices === 'function') {
643     if (utils.isAsyncFn(choices)) return choices;
644     choices = choices.call(prompt, prompt);
645   }
646   for (let choice of choices) {
647     if (Array.isArray(choice.choices)) {
648       let items = choice.choices.filter(ch => !prompt.isDisabled(ch));
649       choice.enabled = items.every(ch => ch.enabled === true);
650     }
651     if (prompt.isDisabled(choice) === true) {
652       delete choice.enabled;
653     }
654   }
655   return choices;
656 }
657
658 module.exports = ArrayPrompt;