list.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. /**
  2. * @import {Info, State} from 'mdast-util-to-markdown'
  3. * @import {List, Parents} from 'mdast'
  4. */
  5. import {checkBullet} from '../util/check-bullet.js'
  6. import {checkBulletOther} from '../util/check-bullet-other.js'
  7. import {checkBulletOrdered} from '../util/check-bullet-ordered.js'
  8. import {checkRule} from '../util/check-rule.js'
  9. /**
  10. * @param {List} node
  11. * @param {Parents | undefined} parent
  12. * @param {State} state
  13. * @param {Info} info
  14. * @returns {string}
  15. */
  16. export function list(node, parent, state, info) {
  17. const exit = state.enter('list')
  18. const bulletCurrent = state.bulletCurrent
  19. /** @type {string} */
  20. let bullet = node.ordered ? checkBulletOrdered(state) : checkBullet(state)
  21. /** @type {string} */
  22. const bulletOther = node.ordered
  23. ? bullet === '.'
  24. ? ')'
  25. : '.'
  26. : checkBulletOther(state)
  27. let useDifferentMarker =
  28. parent && state.bulletLastUsed ? bullet === state.bulletLastUsed : false
  29. if (!node.ordered) {
  30. const firstListItem = node.children ? node.children[0] : undefined
  31. // If there’s an empty first list item directly in two list items,
  32. // we have to use a different bullet:
  33. //
  34. // ```markdown
  35. // * - *
  36. // ```
  37. //
  38. // …because otherwise it would become one big thematic break.
  39. if (
  40. // Bullet could be used as a thematic break marker:
  41. (bullet === '*' || bullet === '-') &&
  42. // Empty first list item:
  43. firstListItem &&
  44. (!firstListItem.children || !firstListItem.children[0]) &&
  45. // Directly in two other list items:
  46. state.stack[state.stack.length - 1] === 'list' &&
  47. state.stack[state.stack.length - 2] === 'listItem' &&
  48. state.stack[state.stack.length - 3] === 'list' &&
  49. state.stack[state.stack.length - 4] === 'listItem' &&
  50. // That are each the first child.
  51. state.indexStack[state.indexStack.length - 1] === 0 &&
  52. state.indexStack[state.indexStack.length - 2] === 0 &&
  53. state.indexStack[state.indexStack.length - 3] === 0
  54. ) {
  55. useDifferentMarker = true
  56. }
  57. // If there’s a thematic break at the start of the first list item,
  58. // we have to use a different bullet:
  59. //
  60. // ```markdown
  61. // * ---
  62. // ```
  63. //
  64. // …because otherwise it would become one big thematic break.
  65. if (checkRule(state) === bullet && firstListItem) {
  66. let index = -1
  67. while (++index < node.children.length) {
  68. const item = node.children[index]
  69. if (
  70. item &&
  71. item.type === 'listItem' &&
  72. item.children &&
  73. item.children[0] &&
  74. item.children[0].type === 'thematicBreak'
  75. ) {
  76. useDifferentMarker = true
  77. break
  78. }
  79. }
  80. }
  81. }
  82. if (useDifferentMarker) {
  83. bullet = bulletOther
  84. }
  85. state.bulletCurrent = bullet
  86. const value = state.containerFlow(node, info)
  87. state.bulletLastUsed = bullet
  88. state.bulletCurrent = bulletCurrent
  89. exit()
  90. return value
  91. }