Repositorio del curso CCOM4030 el semestre B91 del proyecto Artesanías con el Instituto de Cultura

screen-manager.js 3.8KB

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