aboutsummaryrefslogtreecommitdiff
path: root/src/node.c
blob: 489ac7ccdd6f46e696d5a93a94f585a42eae96ca (plain)
  1. #include <stddef.h>
  2. #include "config.h"
  3. #include "node.h"
  4. cmark_node_type
  5. cmark_node_get_type(cmark_node *node)
  6. {
  7. return node->type;
  8. }
  9. static const char*
  10. S_type_string(cmark_node *node)
  11. {
  12. switch (node->type) {
  13. case CMARK_NODE_DOCUMENT: return "DOCUMENT";
  14. case CMARK_NODE_BQUOTE: return "BQUOTE";
  15. case CMARK_NODE_LIST: return "LIST";
  16. case CMARK_NODE_LIST_ITEM: return "LIST_ITEM";
  17. case CMARK_NODE_FENCED_CODE: return "FENCED_CODE";
  18. case CMARK_NODE_INDENTED_CODE: return "INDENTED_CODE";
  19. case CMARK_NODE_HTML: return "HTML";
  20. case CMARK_NODE_PARAGRAPH: return "PARAGRAPH";
  21. case CMARK_NODE_ATX_HEADER: return "ATX_HEADER";
  22. case CMARK_NODE_SETEXT_HEADER: return "SETEXT_HEADER";
  23. case CMARK_NODE_HRULE: return "HRULE";
  24. case CMARK_NODE_REFERENCE_DEF: return "REFERENCE_DEF";
  25. case CMARK_NODE_STRING: return "STRING";
  26. case CMARK_NODE_SOFTBREAK: return "SOFTBREAK";
  27. case CMARK_NODE_LINEBREAK: return "LINEBREAK";
  28. case CMARK_NODE_INLINE_CODE: return "INLINE_CODE";
  29. case CMARK_NODE_INLINE_HTML: return "INLINE_HTML";
  30. case CMARK_NODE_EMPH: return "EMPH";
  31. case CMARK_NODE_STRONG: return "STRONG";
  32. case CMARK_NODE_LINK: return "LINK";
  33. case CMARK_NODE_IMAGE: return "IMAGE";
  34. }
  35. return "<unknown>";
  36. }
  37. cmark_node*
  38. cmark_node_next(cmark_node *node)
  39. {
  40. return node->next;
  41. }
  42. cmark_node*
  43. cmark_node_previous(cmark_node *node)
  44. {
  45. return node->prev;
  46. }
  47. cmark_node*
  48. cmark_node_parent(cmark_node *node)
  49. {
  50. return node->parent;
  51. }
  52. cmark_node*
  53. cmark_node_first_child(cmark_node *node)
  54. {
  55. return node->first_child;
  56. }
  57. cmark_node*
  58. cmark_node_last_child(cmark_node *node)
  59. {
  60. return node->last_child;
  61. }
  62. static inline bool
  63. S_is_block(cmark_node *node) {
  64. return node->type >= CMARK_NODE_FIRST_BLOCK
  65. && node->type <= CMARK_NODE_LAST_BLOCK;
  66. }
  67. static inline bool
  68. S_is_inline(cmark_node *node) {
  69. return node->type >= CMARK_NODE_FIRST_INLINE
  70. && node->type <= CMARK_NODE_LAST_INLINE;
  71. }
  72. static bool
  73. S_can_contain(cmark_node *node, cmark_node *child)
  74. {
  75. if (child->type == CMARK_NODE_DOCUMENT) {
  76. return false;
  77. }
  78. switch (node->type) {
  79. case CMARK_NODE_DOCUMENT:
  80. case CMARK_NODE_BQUOTE:
  81. case CMARK_NODE_LIST_ITEM:
  82. return S_is_block(child)
  83. && child->type != CMARK_NODE_LIST_ITEM;
  84. case CMARK_NODE_LIST:
  85. return child->type == CMARK_NODE_LIST_ITEM;
  86. case CMARK_NODE_PARAGRAPH:
  87. case CMARK_NODE_ATX_HEADER:
  88. case CMARK_NODE_SETEXT_HEADER:
  89. case CMARK_NODE_EMPH:
  90. case CMARK_NODE_STRONG:
  91. case CMARK_NODE_LINK:
  92. case CMARK_NODE_IMAGE:
  93. return S_is_inline(child);
  94. default:
  95. break;
  96. }
  97. return false;
  98. }
  99. // Unlink a node without adjusting its next, prev, and parent pointers.
  100. static void
  101. S_node_unlink(cmark_node *node)
  102. {
  103. if (node->prev) {
  104. node->prev->next = node->next;
  105. }
  106. if (node->next) {
  107. node->next->prev = node->prev;
  108. }
  109. // Adjust first_child and last_child of parent.
  110. cmark_node *parent = node->parent;
  111. if (parent) {
  112. if (parent->first_child == node) {
  113. parent->first_child = node->next;
  114. }
  115. if (parent->last_child == node) {
  116. parent->last_child = node->prev;
  117. }
  118. }
  119. }
  120. void
  121. cmark_node_unlink(cmark_node *node) {
  122. S_node_unlink(node);
  123. node->next = NULL;
  124. node->prev = NULL;
  125. node->parent = NULL;
  126. }
  127. int
  128. cmark_node_insert_before(cmark_node *node, cmark_node *sibling)
  129. {
  130. if (!S_can_contain(node->parent, sibling)) {
  131. return 0;
  132. }
  133. S_node_unlink(sibling);
  134. cmark_node *old_prev = node->prev;
  135. // Insert 'sibling' between 'old_prev' and 'node'.
  136. if (old_prev) {
  137. old_prev->next = sibling;
  138. }
  139. sibling->prev = old_prev;
  140. sibling->next = node;
  141. node->prev = sibling;
  142. // Set new parent.
  143. cmark_node *parent = node->parent;
  144. sibling->parent = parent;
  145. // Adjust first_child of parent if inserted as first child.
  146. if (parent && !old_prev) {
  147. parent->first_child = sibling;
  148. }
  149. return 1;
  150. }
  151. int
  152. cmark_node_insert_after(cmark_node *node, cmark_node *sibling)
  153. {
  154. if (!S_can_contain(node->parent, sibling)) {
  155. return 0;
  156. }
  157. S_node_unlink(sibling);
  158. cmark_node *old_next = node->next;
  159. // Insert 'sibling' between 'node' and 'old_next'.
  160. if (old_next) {
  161. old_next->prev = sibling;
  162. }
  163. sibling->next = old_next;
  164. sibling->prev = node;
  165. node->next = sibling;
  166. // Set new parent.
  167. cmark_node *parent = node->parent;
  168. sibling->parent = parent;
  169. // Adjust last_child of parent if inserted as last child.
  170. if (parent && !old_next) {
  171. parent->last_child = sibling;
  172. }
  173. return 1;
  174. }
  175. int
  176. cmark_node_prepend_child(cmark_node *node, cmark_node *child)
  177. {
  178. if (!S_can_contain(node, child)) {
  179. return 0;
  180. }
  181. S_node_unlink(child);
  182. cmark_node *old_first_child = node->first_child;
  183. child->next = old_first_child;
  184. child->prev = NULL;
  185. child->parent = node;
  186. node->first_child = child;
  187. if (old_first_child) {
  188. old_first_child->prev = child;
  189. }
  190. else {
  191. // Also set last_child if node previously had no children.
  192. node->last_child = child;
  193. }
  194. return 1;
  195. }
  196. int
  197. cmark_node_append_child(cmark_node *node, cmark_node *child)
  198. {
  199. if (!S_can_contain(node, child)) {
  200. return 0;
  201. }
  202. S_node_unlink(child);
  203. cmark_node *old_last_child = node->last_child;
  204. child->next = NULL;
  205. child->prev = old_last_child;
  206. child->parent = node;
  207. node->last_child = child;
  208. if (old_last_child) {
  209. old_last_child->next = child;
  210. }
  211. else {
  212. // Also set first_child if node previously had no children.
  213. node->first_child = child;
  214. }
  215. return 1;
  216. }
  217. static void
  218. S_print_error(cmark_node *node, const char *elem)
  219. {
  220. fprintf(stderr, "Invalid '%s' in node type %s at %d:%d\n", elem,
  221. S_type_string(node), node->start_line, node->start_column);
  222. }
  223. int
  224. cmark_node_check(cmark_node *node)
  225. {
  226. cmark_node *cur = node;
  227. int errors = 0;
  228. while (cur) {
  229. if (cur->first_child) {
  230. if (cur->first_child->parent != cur) {
  231. S_print_error(cur->first_child, "parent");
  232. cur->first_child->parent = cur;
  233. ++errors;
  234. }
  235. cur = cur->first_child;
  236. }
  237. else if (cur->next) {
  238. if (cur->next->prev != cur) {
  239. S_print_error(cur->next, "prev");
  240. cur->next->prev = cur;
  241. ++errors;
  242. }
  243. if (cur->next->parent != cur->parent) {
  244. S_print_error(cur->next, "parent");
  245. cur->next->parent = cur->parent;
  246. ++errors;
  247. }
  248. cur = cur->next;
  249. }
  250. else {
  251. if (cur->parent->last_child != cur) {
  252. S_print_error(cur->parent, "last_child");
  253. cur->parent->last_child = cur;
  254. ++errors;
  255. }
  256. cmark_node *ancestor = cur->parent;
  257. cur = NULL;
  258. while (ancestor != node->parent) {
  259. if (ancestor->next) {
  260. cur = ancestor->next;
  261. break;
  262. }
  263. ancestor = ancestor->parent;
  264. }
  265. }
  266. }
  267. return errors;
  268. }
  269. // Free a cmark_node list and any children.
  270. void cmark_free_nodes(cmark_node *e)
  271. {
  272. cmark_node *next;
  273. while (e != NULL) {
  274. strbuf_free(&e->string_content);
  275. switch (e->type){
  276. case NODE_FENCED_CODE:
  277. strbuf_free(&e->as.code.info);
  278. break;
  279. case NODE_STRING:
  280. case NODE_INLINE_HTML:
  281. case NODE_INLINE_CODE:
  282. cmark_chunk_free(&e->as.literal);
  283. break;
  284. case NODE_LINK:
  285. case NODE_IMAGE:
  286. free(e->as.link.url);
  287. free(e->as.link.title);
  288. break;
  289. default:
  290. break;
  291. }
  292. if (e->last_child) {
  293. // Splice children into list
  294. e->last_child->next = e->next;
  295. e->next = e->first_child;
  296. }
  297. next = e->next;
  298. free(e);
  299. e = next;
  300. }
  301. }