list.js 3.0 KB

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