ansi.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. 'use strict';
  2. const isTerm = process.env.TERM_PROGRAM === 'Apple_Terminal';
  3. const colors = require('ansi-colors');
  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. restore(state = {}) {
  56. let { after, cursor, initial, input, prompt, size, value } = state;
  57. initial = utils.isPrimitive(initial) ? String(initial) : '';
  58. input = utils.isPrimitive(input) ? String(input) : '';
  59. value = utils.isPrimitive(value) ? String(value) : '';
  60. if (size) {
  61. let codes = ansi.cursor.up(size) + ansi.cursor.to(prompt.length);
  62. let diff = input.length - cursor;
  63. if (diff > 0) {
  64. codes += ansi.cursor.left(diff);
  65. }
  66. return codes;
  67. }
  68. if (value || after) {
  69. let pos = (!input && !!initial) ? -initial.length : -input.length + cursor;
  70. if (after) pos -= after.length;
  71. if (input === '' && initial && !prompt.includes(initial)) {
  72. pos += initial.length;
  73. }
  74. return ansi.cursor.move(pos);
  75. }
  76. }
  77. };
  78. const erase = ansi.erase = {
  79. screen: code.screen,
  80. up: code.up,
  81. down: code.down,
  82. line: code.line,
  83. lineEnd: code.lineEnd,
  84. lineStart: code.lineStart,
  85. lines(n) {
  86. let str = '';
  87. for (let i = 0; i < n; i++) {
  88. str += ansi.erase.line + (i < n - 1 ? ansi.cursor.up(1) : '');
  89. }
  90. if (n) str += ansi.code.beginning;
  91. return str;
  92. }
  93. };
  94. ansi.clear = (input = '', columns = process.stdout.columns) => {
  95. if (!columns) return erase.line + cursor.to(0);
  96. let width = str => [...colors.unstyle(str)].length;
  97. let lines = input.split(/\r?\n/);
  98. let rows = 0;
  99. for (let line of lines) {
  100. rows += 1 + Math.floor(Math.max(width(line) - 1, 0) / columns);
  101. }
  102. return (erase.line + cursor.prevLine()).repeat(rows - 1) + erase.line + cursor.to(0);
  103. };