ansi.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. 'use strict';
  2. const isTerm = process.env.TERM_PROGRAM === 'Apple_Terminal';
  3. const stripAnsi = require('strip-ansi');
  4. const utils = require('./utils');
  5. const ansi = module.exports = exports;
  6. const ESC = '\u001b[';
  7. const BEL = '\u0007';
  8. let hidden = false;
  9. const code = ansi.code = {
  10. bell: BEL,
  11. beep: BEL,
  12. beginning: `${ESC}G`,
  13. down: `${ESC}J`,
  14. esc: ESC,
  15. getPosition: `${ESC}6n`,
  16. hide: `${ESC}?25l`,
  17. line: `${ESC}2K`,
  18. lineEnd: `${ESC}K`,
  19. lineStart: `${ESC}1K`,
  20. restorePosition: ESC + (isTerm ? '8' : 'u'),
  21. savePosition: ESC + (isTerm ? '7' : 's'),
  22. screen: `${ESC}2J`,
  23. show: `${ESC}?25h`,
  24. up: `${ESC}1J`
  25. };
  26. const cursor = ansi.cursor = {
  27. get hidden() {
  28. return hidden;
  29. },
  30. hide() {
  31. hidden = true;
  32. return code.hide;
  33. },
  34. show() {
  35. hidden = false;
  36. return code.show;
  37. },
  38. forward: (count = 1) => `${ESC}${count}C`,
  39. backward: (count = 1) => `${ESC}${count}D`,
  40. nextLine: (count = 1) => `${ESC}E`.repeat(count),
  41. prevLine: (count = 1) => `${ESC}F`.repeat(count),
  42. up: (count = 1) => count ? `${ESC}${count}A` : '',
  43. down: (count = 1) => count ? `${ESC}${count}B` : '',
  44. right: (count = 1) => count ? `${ESC}${count}C` : '',
  45. left: (count = 1) => count ? `${ESC}${count}D` : '',
  46. to(x, y) {
  47. return y ? `${ESC}${y + 1};${x + 1}H` : `${ESC}${x + 1}G`;
  48. },
  49. move(x = 0, y = 0) {
  50. let res = '';
  51. res += (x < 0) ? cursor.left(-x) : (x > 0) ? cursor.right(x) : '';
  52. res += (y < 0) ? cursor.up(-y) : (y > 0) ? cursor.down(y) : '';
  53. return res;
  54. },
  55. strLen(str) {
  56. // to suport chinese
  57. var realLength = 0, len = str.length, charCode = -1;
  58. for (var i = 0; i < len; i++) {
  59. charCode = str.charCodeAt(i);
  60. if (charCode >= 0 && charCode <= 128) realLength += 1;
  61. else realLength += 2;
  62. }
  63. return realLength;
  64. },
  65. restore(state = {}) {
  66. let { after, cursor, initial, input, prompt, size, value } = state;
  67. initial = utils.isPrimitive(initial) ? String(initial) : '';
  68. input = utils.isPrimitive(input) ? String(input) : '';
  69. value = utils.isPrimitive(value) ? String(value) : '';
  70. if (size) {
  71. let codes = ansi.cursor.up(size) + ansi.cursor.to(this.strLen(prompt));
  72. let diff = input.length - cursor;
  73. if (diff > 0) {
  74. codes += ansi.cursor.left(diff);
  75. }
  76. return codes;
  77. }
  78. if (value || after) {
  79. let pos = (!input && !!initial) ? - this.strLen(initial) : -this.strLen(input) + cursor;
  80. if (after) pos -= this.strLen(after);
  81. if (input === '' && initial && !prompt.includes(initial)) {
  82. pos += this.strLen(initial);
  83. }
  84. return ansi.cursor.move(pos);
  85. }
  86. }
  87. };
  88. const erase = ansi.erase = {
  89. screen: code.screen,
  90. up: code.up,
  91. down: code.down,
  92. line: code.line,
  93. lineEnd: code.lineEnd,
  94. lineStart: code.lineStart,
  95. lines(n) {
  96. let str = '';
  97. for (let i = 0; i < n; i++) {
  98. str += ansi.erase.line + (i < n - 1 ? ansi.cursor.up(1) : '');
  99. }
  100. if (n) str += ansi.code.beginning;
  101. return str;
  102. }
  103. };
  104. ansi.clear = (input = '', columns = process.stdout.columns) => {
  105. if (!columns) return erase.line + cursor.to(0);
  106. let width = str => [...stripAnsi(str)].length;
  107. let lines = input.split(/\r?\n/);
  108. let rows = 0;
  109. for (let line of lines) {
  110. rows += 1 + Math.floor(Math.max(width(line) - 1, 0) / columns);
  111. }
  112. return (erase.line + cursor.prevLine()).repeat(rows - 1) + erase.line + cursor.to(0);
  113. };