inline-code.js 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. /**
  2. * @typedef {import('mdast').InlineCode} InlineCode
  3. * @typedef {import('mdast').Parents} Parents
  4. * @typedef {import('../types.js').State} State
  5. */
  6. inlineCode.peek = inlineCodePeek
  7. /**
  8. * @param {InlineCode} node
  9. * @param {Parents | undefined} _
  10. * @param {State} state
  11. * @returns {string}
  12. */
  13. export function inlineCode(node, _, state) {
  14. let value = node.value || ''
  15. let sequence = '`'
  16. let index = -1
  17. // If there is a single grave accent on its own in the code, use a fence of
  18. // two.
  19. // If there are two in a row, use one.
  20. while (new RegExp('(^|[^`])' + sequence + '([^`]|$)').test(value)) {
  21. sequence += '`'
  22. }
  23. // If this is not just spaces or eols (tabs don’t count), and either the
  24. // first or last character are a space, eol, or tick, then pad with spaces.
  25. if (
  26. /[^ \r\n]/.test(value) &&
  27. ((/^[ \r\n]/.test(value) && /[ \r\n]$/.test(value)) || /^`|`$/.test(value))
  28. ) {
  29. value = ' ' + value + ' '
  30. }
  31. // We have a potential problem: certain characters after eols could result in
  32. // blocks being seen.
  33. // For example, if someone injected the string `'\n# b'`, then that would
  34. // result in an ATX heading.
  35. // We can’t escape characters in `inlineCode`, but because eols are
  36. // transformed to spaces when going from markdown to HTML anyway, we can swap
  37. // them out.
  38. while (++index < state.unsafe.length) {
  39. const pattern = state.unsafe[index]
  40. const expression = state.compilePattern(pattern)
  41. /** @type {RegExpExecArray | null} */
  42. let match
  43. // Only look for `atBreak`s.
  44. // Btw: note that `atBreak` patterns will always start the regex at LF or
  45. // CR.
  46. if (!pattern.atBreak) continue
  47. while ((match = expression.exec(value))) {
  48. let position = match.index
  49. // Support CRLF (patterns only look for one of the characters).
  50. if (
  51. value.charCodeAt(position) === 10 /* `\n` */ &&
  52. value.charCodeAt(position - 1) === 13 /* `\r` */
  53. ) {
  54. position--
  55. }
  56. value = value.slice(0, position) + ' ' + value.slice(match.index + 1)
  57. }
  58. }
  59. return sequence + value + sequence
  60. }
  61. /**
  62. * @returns {string}
  63. */
  64. function inlineCodePeek() {
  65. return '`'
  66. }