code-text.mjs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. var codeText = {
  2. name: 'codeText',
  3. tokenize: tokenizeCodeText,
  4. resolve: resolveCodeText,
  5. previous: previous
  6. }
  7. export default codeText
  8. import assert from 'assert'
  9. import codes from '../character/codes.mjs'
  10. import markdownLineEnding from '../character/markdown-line-ending.mjs'
  11. import types from '../constant/types.mjs'
  12. function resolveCodeText(events) {
  13. var tailExitIndex = events.length - 4
  14. var headEnterIndex = 3
  15. var index
  16. var enter
  17. // If we start and end with an EOL or a space.
  18. if (
  19. (events[headEnterIndex][1].type === types.lineEnding ||
  20. events[headEnterIndex][1].type === 'space') &&
  21. (events[tailExitIndex][1].type === types.lineEnding ||
  22. events[tailExitIndex][1].type === 'space')
  23. ) {
  24. index = headEnterIndex
  25. // And we have data.
  26. while (++index < tailExitIndex) {
  27. if (events[index][1].type === types.codeTextData) {
  28. // Then we have padding.
  29. events[tailExitIndex][1].type = events[headEnterIndex][1].type =
  30. types.codeTextPadding
  31. headEnterIndex += 2
  32. tailExitIndex -= 2
  33. break
  34. }
  35. }
  36. }
  37. // Merge adjacent spaces and data.
  38. index = headEnterIndex - 1
  39. tailExitIndex++
  40. while (++index <= tailExitIndex) {
  41. if (enter === undefined) {
  42. if (
  43. index !== tailExitIndex &&
  44. events[index][1].type !== types.lineEnding
  45. ) {
  46. enter = index
  47. }
  48. } else if (
  49. index === tailExitIndex ||
  50. events[index][1].type === types.lineEnding
  51. ) {
  52. events[enter][1].type = types.codeTextData
  53. if (index !== enter + 2) {
  54. events[enter][1].end = events[index - 1][1].end
  55. events.splice(enter + 2, index - enter - 2)
  56. tailExitIndex -= index - enter - 2
  57. index = enter + 2
  58. }
  59. enter = undefined
  60. }
  61. }
  62. return events
  63. }
  64. function previous(code) {
  65. // If there is a previous code, there will always be a tail.
  66. return (
  67. code !== codes.graveAccent ||
  68. this.events[this.events.length - 1][1].type === types.characterEscape
  69. )
  70. }
  71. function tokenizeCodeText(effects, ok, nok) {
  72. var self = this
  73. var sizeOpen = 0
  74. var size
  75. var token
  76. return start
  77. function start(code) {
  78. assert(code === codes.graveAccent, 'expected `` ` ``')
  79. assert(previous.call(self, self.previous), 'expected correct previous')
  80. effects.enter(types.codeText)
  81. effects.enter(types.codeTextSequence)
  82. return openingSequence(code)
  83. }
  84. function openingSequence(code) {
  85. if (code === codes.graveAccent) {
  86. effects.consume(code)
  87. sizeOpen++
  88. return openingSequence
  89. }
  90. effects.exit(types.codeTextSequence)
  91. return gap(code)
  92. }
  93. function gap(code) {
  94. // EOF.
  95. if (code === codes.eof) {
  96. return nok(code)
  97. }
  98. // Closing fence?
  99. // Could also be data.
  100. if (code === codes.graveAccent) {
  101. token = effects.enter(types.codeTextSequence)
  102. size = 0
  103. return closingSequence(code)
  104. }
  105. // Tabs don’t work, and virtual spaces don’t make sense.
  106. if (code === codes.space) {
  107. effects.enter('space')
  108. effects.consume(code)
  109. effects.exit('space')
  110. return gap
  111. }
  112. if (markdownLineEnding(code)) {
  113. effects.enter(types.lineEnding)
  114. effects.consume(code)
  115. effects.exit(types.lineEnding)
  116. return gap
  117. }
  118. // Data.
  119. effects.enter(types.codeTextData)
  120. return data(code)
  121. }
  122. // In code.
  123. function data(code) {
  124. if (
  125. code === codes.eof ||
  126. code === codes.space ||
  127. code === codes.graveAccent ||
  128. markdownLineEnding(code)
  129. ) {
  130. effects.exit(types.codeTextData)
  131. return gap(code)
  132. }
  133. effects.consume(code)
  134. return data
  135. }
  136. // Closing fence.
  137. function closingSequence(code) {
  138. // More.
  139. if (code === codes.graveAccent) {
  140. effects.consume(code)
  141. size++
  142. return closingSequence
  143. }
  144. // Done!
  145. if (size === sizeOpen) {
  146. effects.exit(types.codeTextSequence)
  147. effects.exit(types.codeText)
  148. return ok(code)
  149. }
  150. // More or less accents: mark as data.
  151. token.type = types.codeTextData
  152. return data(code)
  153. }
  154. }