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