Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix duplicate output when more than 1 line (fixes #153) #168

Merged
merged 12 commits into from
Jul 5, 2019
52 changes: 19 additions & 33 deletions lib/elements/autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

const color = require('kleur');
const Prompt = require('./prompt');
const { cursor } = require('sisteransi');
const { erase, cursor } = require('sisteransi');
const { style, clear, figures, strip } = require('../util');

const getVal = (arr, i) => arr[i] && (arr[i].value || arr[i].title || arr[i]);
Expand Down Expand Up @@ -214,50 +214,36 @@ class AutocompletePrompt extends Prompt {

render() {
if (this.closed) return;
if (!this.firstRender) this.out.write(clear(this.outputText));
super.render();
if (this.lineCount) this.out.write(cursor.down(this.lineCount));

let prompt = color.bold(`${style.symbol(this.done, this.aborted)} ${this.msg} `)
+ `${style.delimiter(this.completing)} `;
let length = strip(prompt).length;

if (this.done && this.suggestions[this.page][this.select]) {
prompt += `${this.suggestions[this.page][this.select].title}`;
} else {
this.rendered = `${this.transform.render(this.input)}`;
length += this.rendered.length;
prompt += this.rendered;
}
this.outputText = [
color.bold(style.symbol(this.done, this.aborted)),
color.bold(this.msg),
style.delimiter(this.completing),
this.done && this.suggestions[this.page][this.select]
? this.suggestions[this.page][this.select].title
: this.rendered = this.transform.render(this.input)
].join(' ');

if (!this.done) {
this.lineCount = this.suggestions[this.page].length;
let suggestions = this.suggestions[this.page].reduce((acc, item, i) =>
acc + `\n${i === this.select ? color.cyan(item.title) : item.title}`, '');
const suggestions = this.suggestions[this.page].reduce(
(acc, item, i) => acc + `\n${i === this.select ? color.cyan(item.title) : item.title}`, '');
if (suggestions && !this.isFallback) {
prompt += suggestions;
this.outputText += suggestions;
if (this.suggestions.length > 1) {
this.lineCount++;
prompt += color.blue(`\nPage ${this.page+1}/${this.suggestions.length}`);
this.outputText += color.blue(`\nPage ${this.page+1}/${this.suggestions.length}`);
}
} else {
const fallbackIndex = getIndex(this.choices, this.fallback);
const fallbackTitle = fallbackIndex !== undefined
? getTitle(this.choices, fallbackIndex)
: this.fallback;
prompt += `\n${color.gray(fallbackTitle)}`;
this.lineCount++;
const fallbackTitle = !isNaN(fallbackIndex)
? getTitle(this.choices, fallbackIndex)
: this.fallback;
this.outputText += `\n${color.gray(fallbackTitle)}`;
}
}

this.out.write(this.clear + prompt);
this.clear = clear(prompt);

if (this.lineCount && !this.done) {
let pos = cursor.up(this.lineCount);
pos += cursor.left+cursor.to(length);
pos += cursor.move(-this.rendered.length+this.cursor*this.scale);
this.out.write(pos);
}
this.out.write(erase.line + cursor.to(0) + this.outputText);
}
}

Expand Down
26 changes: 12 additions & 14 deletions lib/elements/confirm.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const color = require('kleur');
const Prompt = require('./prompt');
const { style } = require('../util');
const { style, clear } = require('../util');
const { erase, cursor } = require('sisteransi');

/**
Expand Down Expand Up @@ -67,20 +67,18 @@ class ConfirmPrompt extends Prompt {
render() {
if (this.closed) return;
if (this.firstRender) this.out.write(cursor.hide);
else this.out.write(clear(this.outputText));
super.render();

this.out.write(
erase.line +
cursor.to(0) +
[
style.symbol(this.done, this.aborted),
color.bold(this.msg),
style.delimiter(this.done),
this.done
? this.value ? this.yesMsg : this.noMsg
: color.gray(this.initialValue ? this.yesOption : this.noOption)
].join(' ')
);

this.outputText = [
style.symbol(this.done, this.aborted),
color.bold(this.msg),
style.delimiter(this.done),
this.done ? (this.value ? this.yesMsg : this.noMsg)
: color.gray(this.initialValue ? this.yesOption : this.noOption)
].join(' ');

this.out.write(erase.line + cursor.to(0) + this.outputText);
}
}

Expand Down
32 changes: 12 additions & 20 deletions lib/elements/date.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

const color = require('kleur');
const Prompt = require('./prompt');
const { style, clear, figures, strip } = require('../util');
const { style, clear, figures } = require('../util');
const { erase, cursor } = require('sisteransi');
const { DatePart, Meridiem, Day, Hours, Milliseconds, Minutes, Month, Seconds, Year } = require('../dateparts');

Expand All @@ -26,6 +26,7 @@ const dfltLocales = {
weekdaysShort: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(',')
}


/**
* DatePrompt Base Element
* @param {Object} opts Options
Expand Down Expand Up @@ -108,7 +109,7 @@ class DatePrompt extends Prompt {
this.out.write('\n');
this.close();
}

async validate() {
let valid = await this.validator(this.value);
if (typeof valid === 'string') {
Expand Down Expand Up @@ -179,34 +180,25 @@ class DatePrompt extends Prompt {
render() {
if (this.closed) return;
if (this.firstRender) this.out.write(cursor.hide);
else this.out.write(erase.lines(1));
else this.out.write(clear(this.outputText));
super.render();
let clear = erase.line + (this.lines ? erase.down(this.lines) : '') + cursor.to(0);
this.lines = 0;

let error = '';
if (this.error) {
let lines = this.errorMsg.split('\n');
error = lines.reduce((a, l, i) => a + `\n${i ? ` ` : figures.pointerSmall} ${color.red().italic(l)}`, ``);
this.lines = lines.length;
}

// Print prompt
let prompt = [
this.outputText = [
style.symbol(this.done, this.aborted),
color.bold(this.msg),
style.delimiter(false),
this.parts.reduce((arr, p, idx) => arr.concat(idx === this.cursor && !this.done ? color.cyan().underline(p.toString()) : p), [])
.join(''),
.join('')
].join(' ');
let position = '';
if (this.lines) {
position += cursor.up(this.lines);
position += cursor.left+cursor.to(strip(prompt).length);

// Print error
if (this.error) {
this.outputText += this.errorMsg.split('\n').reduce(
(a, l, i) => a + `\n${i ? ` ` : figures.pointerSmall} ${color.red().italic(l)}`, ``);
}

this.out.write(clear+prompt+error+position);
this.out.write(erase.line + cursor.to(0) + this.outputText);
}
}

Expand Down
34 changes: 16 additions & 18 deletions lib/elements/number.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const color = require('kleur');
const Prompt = require('./prompt');
const { cursor, erase } = require('sisteransi');
const { style, clear, figures, strip } = require('../util');
const { style, figures, ansi, clear, lines } = require('../util');

const isNumber = /[0-9]/;
const isDef = any => any !== undefined;
Expand Down Expand Up @@ -170,32 +170,30 @@ class NumberPrompt extends Prompt {

render() {
if (this.closed) return;
super.render();
let clear = erase.line + (this.lines ? erase.down(this.lines) : ``) + cursor.to(0);
this.lines = 0;

let error = ``;
if (this.error) {
let lines = this.errorMsg.split(`\n`);
error += lines.reduce((a, l, i) => a + `\n${i ? ` ` : figures.pointerSmall} ${color.red().italic(l)}`, ``);
this.lines = lines.length;
if (!this.firstRender) {
if (this.outputError)
this.out.write(cursor.down(lines(this.outputError) - 1) + clear(this.outputError));
this.out.write(clear(this.outputText));
}
super.render();
this.outputError = '';

let underline = !this.done || (!this.done && !this.placeholder);
let prompt = [
// Print prompt
this.outputText = [
style.symbol(this.done, this.aborted),
color.bold(this.msg),
style.delimiter(this.done),
underline ? color[this.color]().underline(this.rendered) : this.rendered
!this.done || (!this.done && !this.placeholder)
? color[this.color]().underline(this.rendered) : this.rendered
].join(` `);

let position = ``;
if (this.lines) {
position += cursor.up(this.lines);
position += cursor.left+cursor.to(strip(prompt).length);
// Print error
if (this.error) {
this.outputError += this.errorMsg.split(`\n`)
.reduce((a, l, i) => a + `\n${i ? ` ` : figures.pointerSmall} ${color.red().italic(l)}`, ``);
}

this.out.write(clear+prompt+error+position);
this.out.write(erase.line + cursor.to(0) + this.outputText + ansi.save + this.outputError + ansi.restore);
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/elements/prompt.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class Prompt extends EventEmitter {
readline.emitKeypressEvents(this.in, rl);

if (this.in.isTTY) this.in.setRawMode(true);
const isSelect = ['SelectPrompt', 'MultiselectPrompt'].indexOf(this.constructor.name) > -1;
const isSelect = [ 'SelectPrompt', 'MultiselectPrompt' ].indexOf(this.constructor.name) > -1;
const keypress = (str, key) => {
let a = action(key, isSelect);
if (a === false) {
Expand Down
50 changes: 25 additions & 25 deletions lib/elements/select.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const color = require('kleur');
const Prompt = require('./prompt');
const { style, clear, figures } = require('../util');
const { erase, cursor } = require('sisteransi');
const { cursor } = require('sisteransi');

/**
* SelectPrompt Base Element
Expand All @@ -22,7 +22,7 @@ class SelectPrompt extends Prompt {
this.hint = opts.hint || '- Use arrow-keys. Return to submit.';
this.warn = opts.warn || '- This option is disabled';
this.cursor = opts.initial || 0;
this.choices = opts.choices.map((ch, idx) => {
this.choices = opts.choices.map((ch, idx) => {
if (typeof ch === 'string')
ch = {title: ch, value: idx};
return {
Expand Down Expand Up @@ -107,37 +107,37 @@ class SelectPrompt extends Prompt {
render() {
if (this.closed) return;
if (this.firstRender) this.out.write(cursor.hide);
else this.out.write(erase.lines(this.choices.length + 1));
else this.out.write(clear(this.outputText));
super.render();

// Print prompt
this.out.write([
style.symbol(this.done, this.aborted),
color.bold(this.msg),
style.delimiter(false),
this.done ? this.selection.title : this.selection.disabled
? color.yellow(this.warn) : color.gray(this.hint)
].join(' '));
this.outputText = [
style.symbol(this.done, this.aborted),
color.bold(this.msg),
style.delimiter(false),
this.done ? this.selection.title : this.selection.disabled
? color.yellow(this.warn) : color.gray(this.hint)
].join(' ');

// Print choices
if (!this.done) {
this.out.write(
'\n' +
this.outputText += '\n' +
this.choices
.map((v, i) => {
let title, prefix;
if (v.disabled) {
title = this.cursor === i ? color.gray().underline(v.title) : color.strikethrough().gray(v.title);
prefix = this.cursor === i ? color.bold().gray(figures.pointer) + ' ' : ' ';
} else {
title = this.cursor === i ? color.cyan().underline(v.title) : v.title;
prefix = this.cursor === i ? color.cyan(figures.pointer) + ' ' : ' ';
}
return `${prefix} ${title}`;
})
.join('\n')
);
.map((v, i) => {
let title, prefix;
if (v.disabled) {
title = this.cursor === i ? color.gray().underline(v.title) : color.strikethrough().gray(v.title);
prefix = this.cursor === i ? color.bold().gray(figures.pointer) + ' ' : ' ';
} else {
title = this.cursor === i ? color.cyan().underline(v.title) : v.title;
prefix = this.cursor === i ? color.cyan(figures.pointer) + ' ' : ' ';
}
return `${prefix} ${title}`;
})
.join('\n');
}

this.out.write(this.outputText);
}
}

Expand Down
33 changes: 12 additions & 21 deletions lib/elements/text.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const color = require('kleur');
const Prompt = require('./prompt');
const { cursor } = require('sisteransi');
const { style, clear, strip, figures } = require('../util');
const { erase, cursor } = require('sisteransi');
const { style, clear, lines, ansi, figures } = require('../util');

/**
* TextPrompt Base Element
Expand Down Expand Up @@ -154,36 +154,27 @@ class TextPrompt extends Prompt {

render() {
if (this.closed) return;
if (!this.firstRender) {
if (this.outputError)
this.out.write(cursor.down(lines(this.outputError) - 1) + clear(this.outputError));
this.out.write(clear(this.outputText));
}
super.render();
let erase = (this.lines ? cursor.down(this.lines) : ``)+this.clear;
this.lines = 0;
this.outputError = '';

let prompt = [
this.outputText = [
style.symbol(this.done, this.aborted),
color.bold(this.msg),
style.delimiter(this.done),
this.red ? color.red(this.rendered) : this.rendered
].join(` `);

let error = ``;
if (this.error) {
let lines = this.errorMsg.split(`\n`);
error += lines.reduce((a, l, i) => a += `\n${i ? ' ' : figures.pointerSmall} ${color.red().italic(l)}`, ``);
this.lines = lines.length;
}

let position = ``;
if (this.lines) {
position += cursor.up(this.lines);
position += cursor.left+cursor.to(strip(prompt).length);
this.outputError += this.errorMsg.split(`\n`)
.reduce((a, l, i) => a + `\n${i ? ' ' : figures.pointerSmall} ${color.red().italic(l)}`, ``);
}
position += cursor.move(this.placeholder ?
-this.initial.length*this.scale :
-this.rendered.length+this.cursor*this.scale
);

this.out.write(erase+prompt+error+position);
this.clear = clear(prompt+error);
this.out.write(erase.line + cursor.to(0) + this.outputText + ansi.save + this.outputError + ansi.restore);
}
}

Expand Down
Loading