AnsiHighlight.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. /**
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * @flow
  8. * @format
  9. */
  10. import {ansiToJson} from 'anser';
  11. import Text from '../../Text/Text';
  12. import View from '../../Components/View/View';
  13. import * as React from 'react';
  14. import type {TextStyleProp} from 'react-native/Libraries/StyleSheet/StyleSheet';
  15. // Afterglow theme from https://iterm2colorschemes.com/
  16. const COLORS = {
  17. 'ansi-black': 'rgb(27, 27, 27)',
  18. 'ansi-red': 'rgb(187, 86, 83)',
  19. 'ansi-green': 'rgb(144, 157, 98)',
  20. 'ansi-yellow': 'rgb(234, 193, 121)',
  21. 'ansi-blue': 'rgb(125, 169, 199)',
  22. 'ansi-magenta': 'rgb(176, 101, 151)',
  23. 'ansi-cyan': 'rgb(140, 220, 216)',
  24. // Instead of white, use the default color provided to the component
  25. // 'ansi-white': 'rgb(216, 216, 216)',
  26. 'ansi-bright-black': 'rgb(98, 98, 98)',
  27. 'ansi-bright-red': 'rgb(187, 86, 83)',
  28. 'ansi-bright-green': 'rgb(144, 157, 98)',
  29. 'ansi-bright-yellow': 'rgb(234, 193, 121)',
  30. 'ansi-bright-blue': 'rgb(125, 169, 199)',
  31. 'ansi-bright-magenta': 'rgb(176, 101, 151)',
  32. 'ansi-bright-cyan': 'rgb(140, 220, 216)',
  33. 'ansi-bright-white': 'rgb(247, 247, 247)',
  34. };
  35. export default function Ansi({
  36. text,
  37. style,
  38. }: {
  39. text: string,
  40. style: TextStyleProp,
  41. ...
  42. }): React.Node {
  43. let commonWhitespaceLength = Infinity;
  44. const parsedLines = text.split(/\n/).map(line =>
  45. ansiToJson(line, {
  46. json: true,
  47. remove_empty: true,
  48. use_classes: true,
  49. }),
  50. );
  51. parsedLines.map(lines => {
  52. // The third item on each line includes the whitespace of the source code.
  53. // We are looking for the least amount of common whitespace to trim all lines.
  54. // Example: Array [" ", " 96 |", " text", ...]
  55. const match = lines[2] && lines[2]?.content?.match(/^ +/);
  56. const whitespaceLength = (match && match[0]?.length) || 0;
  57. if (whitespaceLength < commonWhitespaceLength) {
  58. commonWhitespaceLength = whitespaceLength;
  59. }
  60. });
  61. const getText = (content, key) => {
  62. if (key === 1) {
  63. // Remove the vertical bar after line numbers
  64. return content.replace(/\| $/, ' ');
  65. } else if (key === 2 && commonWhitespaceLength < Infinity) {
  66. // Remove common whitespace at the beginning of the line
  67. return content.substr(commonWhitespaceLength);
  68. } else {
  69. return content;
  70. }
  71. };
  72. return (
  73. <View style={{flexDirection: 'column'}}>
  74. {parsedLines.map((items, i) => (
  75. <View style={{flexDirection: 'row'}} key={i}>
  76. {items.map((bundle, key) => {
  77. const textStyle =
  78. bundle.fg && COLORS[bundle.fg]
  79. ? {
  80. backgroundColor: bundle.bg && COLORS[bundle.bg],
  81. color: bundle.fg && COLORS[bundle.fg],
  82. }
  83. : {
  84. backgroundColor: bundle.bg && COLORS[bundle.bg],
  85. };
  86. return (
  87. <Text style={[style, textStyle]} key={key}>
  88. {getText(bundle.content, key)}
  89. </Text>
  90. );
  91. })}
  92. </View>
  93. ))}
  94. </View>
  95. );
  96. }