forked from SBoudrias/Inquirer.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
screen-manager.js
112 lines (90 loc) · 3.16 KB
/
screen-manager.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
const cliWidth = require('cli-width');
const stripAnsi = require('strip-ansi');
const stringWidth = require('string-width');
const { cursorShow } = require('ansi-escapes');
const util = require('./readline');
const { breakLines } = require('./utils');
const height = (content) => content.split('\n').length;
const lastLine = (content) => content.split('\n').pop();
module.exports = class ScreenManager {
constructor(rl) {
// These variables are keeping information to allow correct prompt re-rendering
this.height = 0;
this.extraLinesUnderPrompt = 0;
this.rl = rl;
}
render(content, bottomContent) {
this.rl.output.unmute();
this.clean(this.extraLinesUnderPrompt);
/**
* Write message to screen and setPrompt to control backspace
*/
const promptLine = lastLine(content);
const rawPromptLine = stripAnsi(promptLine);
// Remove the rl.line from our prompt. We can't rely on the content of
// rl.line (mainly because of the password prompt), so just rely on it's
// length.
let prompt = rawPromptLine;
if (this.rl.line.length) {
prompt = prompt.slice(0, -this.rl.line.length);
}
this.rl.setPrompt(prompt);
// SetPrompt will change cursor position, now we can get correct value
const cursorPos = this.rl._getCursorPos();
const width = cliWidth({ defaultWidth: 80, output: this.rl.output });
content = breakLines(content, width);
if (bottomContent) {
bottomContent = breakLines(bottomContent, width);
}
// Manually insert an extra line if we're at the end of the line.
// This prevent the cursor from appearing at the beginning of the
// current line.
if (rawPromptLine.length % width === 0) {
content += '\n';
}
const fullContent = content + (bottomContent ? '\n' + bottomContent : '');
this.rl.output.write(fullContent);
/**
* Re-adjust the cursor at the correct position.
*/
// We need to consider parts of the prompt under the cursor as part of the bottom
// content in order to correctly cleanup and re-render.
const promptLineUpDiff = Math.floor(rawPromptLine.length / width) - cursorPos.rows;
const bottomContentHeight =
promptLineUpDiff + (bottomContent ? height(bottomContent) : 0);
if (bottomContentHeight > 0) {
util.up(this.rl, bottomContentHeight);
}
// Reset cursor at the beginning of the line
util.left(this.rl, stringWidth(lastLine(fullContent)));
// Adjust cursor on the right
if (cursorPos.cols > 0) {
util.right(this.rl, cursorPos.cols);
}
/**
* Set up state for next re-rendering
*/
this.extraLinesUnderPrompt = bottomContentHeight;
this.height = height(fullContent);
this.rl.output.mute();
}
clean(extraLines) {
if (extraLines > 0) {
util.down(this.rl, extraLines);
}
util.clearLine(this.rl, this.height);
}
done() {
this.releaseCursor();
this.rl.setPrompt('');
this.rl.output.unmute();
this.rl.output.write('\n');
this.rl.output.write(cursorShow);
this.rl.close();
}
releaseCursor() {
if (this.extraLinesUnderPrompt > 0) {
util.down(this.rl, this.extraLinesUnderPrompt);
}
}
};