table.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. 'use strict';
  2. var whitespace = require('is-whitespace-character');
  3. module.exports = table;
  4. var C_BACKSLASH = '\\';
  5. var C_TICK = '`';
  6. var C_DASH = '-';
  7. var C_PIPE = '|';
  8. var C_COLON = ':';
  9. var C_SPACE = ' ';
  10. var C_NEWLINE = '\n';
  11. var C_TAB = '\t';
  12. var MIN_TABLE_COLUMNS = 1;
  13. var MIN_TABLE_ROWS = 2;
  14. var TABLE_ALIGN_LEFT = 'left';
  15. var TABLE_ALIGN_CENTER = 'center';
  16. var TABLE_ALIGN_RIGHT = 'right';
  17. var TABLE_ALIGN_NONE = null;
  18. function table(eat, value, silent) {
  19. var self = this;
  20. var index;
  21. var alignments;
  22. var alignment;
  23. var subvalue;
  24. var row;
  25. var length;
  26. var lines;
  27. var queue;
  28. var character;
  29. var hasDash;
  30. var align;
  31. var cell;
  32. var preamble;
  33. var count;
  34. var opening;
  35. var now;
  36. var position;
  37. var lineCount;
  38. var line;
  39. var rows;
  40. var table;
  41. var lineIndex;
  42. var pipeIndex;
  43. var first;
  44. /* Exit when not in gfm-mode. */
  45. if (!self.options.gfm) {
  46. return;
  47. }
  48. /* Get the rows.
  49. * Detecting tables soon is hard, so there are some
  50. * checks for performance here, such as the minimum
  51. * number of rows, and allowed characters in the
  52. * alignment row. */
  53. index = 0;
  54. lineCount = 0;
  55. length = value.length + 1;
  56. lines = [];
  57. while (index < length) {
  58. lineIndex = value.indexOf(C_NEWLINE, index);
  59. pipeIndex = value.indexOf(C_PIPE, index + 1);
  60. if (lineIndex === -1) {
  61. lineIndex = value.length;
  62. }
  63. if (pipeIndex === -1 || pipeIndex > lineIndex) {
  64. if (lineCount < MIN_TABLE_ROWS) {
  65. return;
  66. }
  67. break;
  68. }
  69. lines.push(value.slice(index, lineIndex));
  70. lineCount++;
  71. index = lineIndex + 1;
  72. }
  73. /* Parse the alignment row. */
  74. subvalue = lines.join(C_NEWLINE);
  75. alignments = lines.splice(1, 1)[0] || [];
  76. index = 0;
  77. length = alignments.length;
  78. lineCount--;
  79. alignment = false;
  80. align = [];
  81. while (index < length) {
  82. character = alignments.charAt(index);
  83. if (character === C_PIPE) {
  84. hasDash = null;
  85. if (alignment === false) {
  86. if (first === false) {
  87. return;
  88. }
  89. } else {
  90. align.push(alignment);
  91. alignment = false;
  92. }
  93. first = false;
  94. } else if (character === C_DASH) {
  95. hasDash = true;
  96. alignment = alignment || TABLE_ALIGN_NONE;
  97. } else if (character === C_COLON) {
  98. if (alignment === TABLE_ALIGN_LEFT) {
  99. alignment = TABLE_ALIGN_CENTER;
  100. } else if (hasDash && alignment === TABLE_ALIGN_NONE) {
  101. alignment = TABLE_ALIGN_RIGHT;
  102. } else {
  103. alignment = TABLE_ALIGN_LEFT;
  104. }
  105. } else if (!whitespace(character)) {
  106. return;
  107. }
  108. index++;
  109. }
  110. if (alignment !== false) {
  111. align.push(alignment);
  112. }
  113. /* Exit when without enough columns. */
  114. if (align.length < MIN_TABLE_COLUMNS) {
  115. return;
  116. }
  117. /* istanbul ignore if - never used (yet) */
  118. if (silent) {
  119. return true;
  120. }
  121. /* Parse the rows. */
  122. position = -1;
  123. rows = [];
  124. table = eat(subvalue).reset({
  125. type: 'table',
  126. align: align,
  127. children: rows
  128. });
  129. while (++position < lineCount) {
  130. line = lines[position];
  131. row = {type: 'tableRow', children: []};
  132. /* Eat a newline character when this is not the
  133. * first row. */
  134. if (position) {
  135. eat(C_NEWLINE);
  136. }
  137. /* Eat the row. */
  138. eat(line).reset(row, table);
  139. length = line.length + 1;
  140. index = 0;
  141. queue = '';
  142. cell = '';
  143. preamble = true;
  144. count = null;
  145. opening = null;
  146. while (index < length) {
  147. character = line.charAt(index);
  148. if (character === C_TAB || character === C_SPACE) {
  149. if (cell) {
  150. queue += character;
  151. } else {
  152. eat(character);
  153. }
  154. index++;
  155. continue;
  156. }
  157. if (character === '' || character === C_PIPE) {
  158. if (preamble) {
  159. eat(character);
  160. } else {
  161. if (character && opening) {
  162. queue += character;
  163. index++;
  164. continue;
  165. }
  166. if ((cell || character) && !preamble) {
  167. subvalue = cell;
  168. if (queue.length > 1) {
  169. if (character) {
  170. subvalue += queue.slice(0, queue.length - 1);
  171. queue = queue.charAt(queue.length - 1);
  172. } else {
  173. subvalue += queue;
  174. queue = '';
  175. }
  176. }
  177. now = eat.now();
  178. eat(subvalue)({
  179. type: 'tableCell',
  180. children: self.tokenizeInline(cell, now)
  181. }, row);
  182. }
  183. eat(queue + character);
  184. queue = '';
  185. cell = '';
  186. }
  187. } else {
  188. if (queue) {
  189. cell += queue;
  190. queue = '';
  191. }
  192. cell += character;
  193. if (character === C_BACKSLASH && index !== length - 2) {
  194. cell += line.charAt(index + 1);
  195. index++;
  196. }
  197. if (character === C_TICK) {
  198. count = 1;
  199. while (line.charAt(index + 1) === character) {
  200. cell += character;
  201. index++;
  202. count++;
  203. }
  204. if (!opening) {
  205. opening = count;
  206. } else if (count >= opening) {
  207. opening = 0;
  208. }
  209. }
  210. }
  211. preamble = false;
  212. index++;
  213. }
  214. /* Eat the alignment row. */
  215. if (!position) {
  216. eat(C_NEWLINE + alignments);
  217. }
  218. }
  219. return table;
  220. }