Highlight.mjs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import { defineComponent, computed, createVNode as _createVNode } from "vue";
  2. import { createNamespace, makeRequiredProp, makeStringProp, truthProp } from "../utils/index.mjs";
  3. const [name, bem] = createNamespace("highlight");
  4. const highlightProps = {
  5. autoEscape: truthProp,
  6. caseSensitive: Boolean,
  7. highlightClass: String,
  8. highlightTag: makeStringProp("span"),
  9. keywords: makeRequiredProp([String, Array]),
  10. sourceString: makeStringProp(""),
  11. tag: makeStringProp("div"),
  12. unhighlightClass: String,
  13. unhighlightTag: makeStringProp("span")
  14. };
  15. var stdin_default = defineComponent({
  16. name,
  17. props: highlightProps,
  18. setup(props) {
  19. const highlightChunks = computed(() => {
  20. const {
  21. autoEscape,
  22. caseSensitive,
  23. keywords,
  24. sourceString
  25. } = props;
  26. const flags = caseSensitive ? "g" : "gi";
  27. const _keywords = Array.isArray(keywords) ? keywords : [keywords];
  28. let chunks = _keywords.filter((keyword) => keyword).reduce((chunks2, keyword) => {
  29. if (autoEscape) {
  30. keyword = keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  31. }
  32. const regex = new RegExp(keyword, flags);
  33. let match;
  34. while (match = regex.exec(sourceString)) {
  35. const start = match.index;
  36. const end = regex.lastIndex;
  37. if (start >= end) {
  38. regex.lastIndex++;
  39. continue;
  40. }
  41. chunks2.push({
  42. start,
  43. end,
  44. highlight: true
  45. });
  46. }
  47. return chunks2;
  48. }, []);
  49. chunks = chunks.sort((a, b) => a.start - b.start).reduce((chunks2, currentChunk) => {
  50. const prevChunk = chunks2[chunks2.length - 1];
  51. if (!prevChunk || currentChunk.start > prevChunk.end) {
  52. const unhighlightStart = prevChunk ? prevChunk.end : 0;
  53. const unhighlightEnd = currentChunk.start;
  54. if (unhighlightStart !== unhighlightEnd) {
  55. chunks2.push({
  56. start: unhighlightStart,
  57. end: unhighlightEnd,
  58. highlight: false
  59. });
  60. }
  61. chunks2.push(currentChunk);
  62. } else {
  63. prevChunk.end = Math.max(prevChunk.end, currentChunk.end);
  64. }
  65. return chunks2;
  66. }, []);
  67. const lastChunk = chunks[chunks.length - 1];
  68. if (!lastChunk) {
  69. chunks.push({
  70. start: 0,
  71. end: sourceString.length,
  72. highlight: false
  73. });
  74. }
  75. if (lastChunk && lastChunk.end < sourceString.length) {
  76. chunks.push({
  77. start: lastChunk.end,
  78. end: sourceString.length,
  79. highlight: false
  80. });
  81. }
  82. return chunks;
  83. });
  84. const renderContent = () => {
  85. const {
  86. sourceString,
  87. highlightClass,
  88. unhighlightClass,
  89. highlightTag,
  90. unhighlightTag
  91. } = props;
  92. return highlightChunks.value.map((chunk) => {
  93. const {
  94. start,
  95. end,
  96. highlight
  97. } = chunk;
  98. const text = sourceString.slice(start, end);
  99. if (highlight) {
  100. return _createVNode(highlightTag, {
  101. "class": [bem("tag"), highlightClass]
  102. }, {
  103. default: () => [text]
  104. });
  105. }
  106. return _createVNode(unhighlightTag, {
  107. "class": unhighlightClass
  108. }, {
  109. default: () => [text]
  110. });
  111. });
  112. };
  113. return () => {
  114. const {
  115. tag
  116. } = props;
  117. return _createVNode(tag, {
  118. "class": bem()
  119. }, {
  120. default: () => [renderContent()]
  121. });
  122. };
  123. }
  124. });
  125. export {
  126. stdin_default as default,
  127. highlightProps
  128. };