Skip to content

Commit

Permalink
Fix duplicate output when more than 1 line (fixes #153) (#168)
Browse files Browse the repository at this point in the history
* fix(select): duplicate when text > 1 line

* fix(confirm): duplicate when text > 1 line

* fix(date): duplicate when text > 1 line + simplified TextPrompt.render()

* fix(toggle): duplicate when text > 1 line

* fix(number): duplicate when text > 1 line

* fix(autocomplete): duplicate when text > 1 line

* fix(number): cursor pos when error is shown

* fix(prompt): Use existing clear function + export lines function

* fix(date): Formatting

* fix(prompt): formatting

* Updated sisteransi + removed util/ansi.js

* fix(dependency): anticipate next version of sisteransi (1.0.2)
  • Loading branch information
elie-g authored and terkelg committed Jul 5, 2019
1 parent 7a831cf commit 6b1c4ab
Show file tree
Hide file tree
Showing 12 changed files with 293 additions and 189 deletions.
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, 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 + cursor.save + this.outputError + cursor.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, 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 + cursor.save + this.outputError + cursor.restore);
}
}

Expand Down
Loading

0 comments on commit 6b1c4ab

Please sign in to comment.