container-phrasing.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. /**
  2. * @import {Handle, Info, State} from 'mdast-util-to-markdown'
  3. * @import {PhrasingParents} from '../types.js'
  4. */
  5. import {encodeCharacterReference} from './encode-character-reference.js'
  6. /**
  7. * Serialize the children of a parent that contains phrasing children.
  8. *
  9. * These children will be joined flush together.
  10. *
  11. * @param {PhrasingParents} parent
  12. * Parent of flow nodes.
  13. * @param {State} state
  14. * Info passed around about the current state.
  15. * @param {Info} info
  16. * Info on where we are in the document we are generating.
  17. * @returns {string}
  18. * Serialized children, joined together.
  19. */
  20. export function containerPhrasing(parent, state, info) {
  21. const indexStack = state.indexStack
  22. const children = parent.children || []
  23. /** @type {Array<string>} */
  24. const results = []
  25. let index = -1
  26. let before = info.before
  27. /** @type {string | undefined} */
  28. let encodeAfter
  29. indexStack.push(-1)
  30. let tracker = state.createTracker(info)
  31. while (++index < children.length) {
  32. const child = children[index]
  33. /** @type {string} */
  34. let after
  35. indexStack[indexStack.length - 1] = index
  36. if (index + 1 < children.length) {
  37. /** @type {Handle} */
  38. // @ts-expect-error: hush, it’s actually a `zwitch`.
  39. let handle = state.handle.handlers[children[index + 1].type]
  40. /** @type {Handle} */
  41. // @ts-expect-error: hush, it’s actually a `zwitch`.
  42. if (handle && handle.peek) handle = handle.peek
  43. after = handle
  44. ? handle(children[index + 1], parent, state, {
  45. before: '',
  46. after: '',
  47. ...tracker.current()
  48. }).charAt(0)
  49. : ''
  50. } else {
  51. after = info.after
  52. }
  53. // In some cases, html (text) can be found in phrasing right after an eol.
  54. // When we’d serialize that, in most cases that would be seen as html
  55. // (flow).
  56. // As we can’t escape or so to prevent it from happening, we take a somewhat
  57. // reasonable approach: replace that eol with a space.
  58. // See: <https://github.com/syntax-tree/mdast-util-to-markdown/issues/15>
  59. if (
  60. results.length > 0 &&
  61. (before === '\r' || before === '\n') &&
  62. child.type === 'html'
  63. ) {
  64. results[results.length - 1] = results[results.length - 1].replace(
  65. /(\r?\n|\r)$/,
  66. ' '
  67. )
  68. before = ' '
  69. // To do: does this work to reset tracker?
  70. tracker = state.createTracker(info)
  71. tracker.move(results.join(''))
  72. }
  73. let value = state.handle(child, parent, state, {
  74. ...tracker.current(),
  75. after,
  76. before
  77. })
  78. // If we had to encode the first character after the previous node and it’s
  79. // still the same character,
  80. // encode it.
  81. if (encodeAfter && encodeAfter === value.slice(0, 1)) {
  82. value =
  83. encodeCharacterReference(encodeAfter.charCodeAt(0)) + value.slice(1)
  84. }
  85. const encodingInfo = state.attentionEncodeSurroundingInfo
  86. state.attentionEncodeSurroundingInfo = undefined
  87. encodeAfter = undefined
  88. // If we have to encode the first character before the current node and
  89. // it’s still the same character,
  90. // encode it.
  91. if (encodingInfo) {
  92. if (
  93. results.length > 0 &&
  94. encodingInfo.before &&
  95. before === results[results.length - 1].slice(-1)
  96. ) {
  97. results[results.length - 1] =
  98. results[results.length - 1].slice(0, -1) +
  99. encodeCharacterReference(before.charCodeAt(0))
  100. }
  101. if (encodingInfo.after) encodeAfter = after
  102. }
  103. tracker.move(value)
  104. results.push(value)
  105. before = value.slice(-1)
  106. }
  107. indexStack.pop()
  108. return results.join('')
  109. }