| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 | 'use strict';var _ = {  last: require('lodash/last'),  flatten: require('lodash/flatten'),};var util = require('./readline');var cliWidth = require('cli-width');var stripAnsi = require('strip-ansi');var stringWidth = require('string-width');function height(content) {  return content.split('\n').length;}function lastLine(content) {  return _.last(content.split('\n'));}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     */    var promptLine = lastLine(content);    var 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.    var 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    var cursorPos = this.rl._getCursorPos();    var width = this.normalizedCliWidth();    content = this.forceLineReturn(content, width);    if (bottomContent) {      bottomContent = this.forceLineReturn(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';    }    var 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.    var promptLineUpDiff = Math.floor(rawPromptLine.length / width) - cursorPos.rows;    var 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.rl.setPrompt('');    this.rl.output.unmute();    this.rl.output.write('\n');  }  releaseCursor() {    if (this.extraLinesUnderPrompt > 0) {      util.down(this.rl, this.extraLinesUnderPrompt);    }  }  normalizedCliWidth() {    var width = cliWidth({      defaultWidth: 80,      output: this.rl.output,    });    return width;  }  breakLines(lines, width) {    // Break lines who're longer than the cli width so we can normalize the natural line    // returns behavior across terminals.    width = width || this.normalizedCliWidth();    var regex = new RegExp('(?:(?:\\033[[0-9;]*m)*.?){1,' + width + '}', 'g');    return lines.map((line) => {      var chunk = line.match(regex);      // Last match is always empty      chunk.pop();      return chunk || '';    });  }  forceLineReturn(content, width) {    width = width || this.normalizedCliWidth();    return _.flatten(this.breakLines(content.split('\n'), width)).join('\n');  }}module.exports = ScreenManager;
 |