]> uap-core.de Git - note.git/commitdiff
parse MD lists into AST
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Sat, 26 Apr 2025 10:40:28 +0000 (12:40 +0200)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Sat, 26 Apr 2025 10:40:28 +0000 (12:40 +0200)
application/editor.c
application/editor.h
application/tests/test-editor.c
application/tests/test-editor.h
application/tests/testmain.c

index 7bfdec4c01d1001f665a63499cd70ff5d6dfa085..89370cf36b099256a58ceeed3b096c2cb7c47cfe 100644 (file)
@@ -60,6 +60,7 @@ typedef struct MDParserData {
     MDDoc *doc;
     MDPara *p_current;
     CxList *text_stack;
+    int list_depth;
     const CxAllocator *a;
 } MDParserData;
 
@@ -74,6 +75,44 @@ static MDNode* get_current_textnode(MDParserData *p) {
     return NULL;
 }
 
+static void md_node_add_child(MDNode *node, MDNode *child) {
+    cx_linked_list_add((void**)&node->children_begin, (void**)&node->children_end, -1, offsetof(MDNode, next), child);
+    child->parent = node;
+}
+
+static void textstack_update_last_element(CxList *stack_list, MDNode *t) {
+    size_t len = cxListSize(stack_list);
+    if(len > 0) {
+        // replace this, when something like cxListSet is available
+        cxListRemove(stack_list, len-1);
+        cxListAdd(stack_list, t);
+    }
+}
+
+// create a node and add it to the current paragraph node tree
+static MDNode* md_node_create(MDParserData *data) {
+    MDNode *current = get_current_textnode(data);
+    size_t len = cxListSize(data->text_stack);
+    
+    MDNode *node = cxCalloc(data->a, 1, sizeof(MDNode));
+    if(current) {
+        if(current->closed) {
+            current->next = node;
+            if(current->parent) {
+                current->parent->children_end = node;
+            }
+            textstack_update_last_element(data->text_stack, node);
+        } else {
+            md_node_add_child(current, node);
+        }
+        
+    } else {
+        data->p_current->content = node;
+    }
+    cxListAdd(data->text_stack, node);
+    return node;
+}
+
 
 static int md_enter_block(MD_BLOCKTYPE type, void* detail, void* userdata) {
     MDParserData *data = userdata;
@@ -81,7 +120,21 @@ static int md_enter_block(MD_BLOCKTYPE type, void* detail, void* userdata) {
         return 0;
     }
     
-    // TODO: li
+    if(type == MD_BLOCK_UL || type == MD_BLOCK_OL) {
+        // UL can be inserted as paragraph (toplevel list) or as
+        // MDNode (nested list)
+        if(++data->list_depth > 1) {
+            // TODO
+            //MDNode *node = cxCalloc(data->a, 1, sizeof(MDNode));
+            //node->type0 = MD_BLOCK_UL;
+            return 0;
+        }
+    } else if(type == MD_BLOCK_LI) {
+        // LI list elements are always added as MDNode*
+        MDNode *node = md_node_create(data);
+        node->type0 = type;
+        return 0;
+    }
     
     // create empty paragraph node and add it to the document
     MDPara *p = cxMalloc(data->a, sizeof(MDPara));
@@ -107,46 +160,16 @@ static int md_enter_block(MD_BLOCKTYPE type, void* detail, void* userdata) {
     return 0;
 }
 
-static int md_leave_block(MD_BLOCKTYPE type, void* detail, void* userdata) {
-    return 0;
-}
+static int md_leave_span(MD_SPANTYPE type, void* detail, void* userdata);
 
-static void textstack_update_last_element(CxList *stack_list, MDNode *t) {
-    size_t len = cxListSize(stack_list);
-    if(len > 0) {
-        // replace this, when something like cxListSet is available
-        cxListRemove(stack_list, len-1);
-        cxListAdd(stack_list, t);
-    }
-}
-
-static void md_node_add_child(MDNode *node, MDNode *child) {
-    cx_linked_list_add((void**)&node->children_begin, (void**)&node->children_end, -1, offsetof(MDNode, next), child);
-    child->parent = node;
-}
-
-// create a node and add it to the current paragraph node tree
-static MDNode* md_node_create(MDParserData *data) {
-    MDNode *current = get_current_textnode(data);
-    size_t len = cxListSize(data->text_stack);
-    
-    MDNode *node = cxCalloc(data->a, 1, sizeof(MDNode));
-    if(current) {
-        if(current->closed) {
-            current->next = node;
-            if(current->parent) {
-                current->parent->children_end = node;
-            }
-            textstack_update_last_element(data->text_stack, node);
-        } else {
-            md_node_add_child(current, node);
-        }
-        
-    } else {
-        data->p_current->content = node;
+static int md_leave_block(MD_BLOCKTYPE type, void* detail, void* userdata) {
+    MDParserData *data = userdata;
+    if(type == MD_BLOCK_UL || type == MD_BLOCK_OL) {
+        data->list_depth--;
+    } else if(type == MD_BLOCK_LI) {
+        md_leave_span(0, NULL, data);
     }
-    cxListAdd(data->text_stack, node);
-    return node;
+    return 0;
 }
 
 static int md_enter_span(MD_SPANTYPE type, void* detail, void* userdata) {
@@ -264,6 +287,7 @@ MDDoc* parse_markdown(cxstring markdown) {
     data.doc = doc;
     data.p_current = NULL;
     data.text_stack = cxArrayListCreateSimple(CX_STORE_POINTERS, 16);
+    data.list_depth = 0;
     data.a = a;
     
     MD_PARSER parser = {0};
@@ -355,6 +379,7 @@ static const char* paragraph_style(MDPara *p) {
         }
         case MD_BLOCK_QUOTE: return EDITOR_STYLE_QUOTE;
         case MD_BLOCK_CODE: return EDITOR_STYLE_CODE_BLOCK;
+        case MD_BLOCK_UL: return EDITOR_STYLE_LIST0;
         default: return EDITOR_STYLE_PARAGRAPH;
     }
 }
index ae4bb996022e8588db828b2959967d351ba99290..954c947ae41d9e6e758bfd006d7f9fb0f08aeb90 100644 (file)
@@ -77,6 +77,7 @@ struct MDNode {
     MDNode *parent;
     cxmutstr text;
     cxmutstr link;
+    MD_BLOCKTYPE type0;
     MD_SPANTYPE type;
     MDNode *children_begin;
     MDNode *children_end;
index 6d275ec7ac52b9396829ae419512da955140ccf0..a0b274be2bf33b4cd73fe9daaaeff1f85b0c38d0 100644 (file)
@@ -241,6 +241,60 @@ CX_TEST(test_parse_markdown_formatting_nested) {
     }
 }
 
+CX_TEST(test_parse_markdown_list) {
+    MDDoc *doc;
+    MDPara *p0; // Test Paragraph
+    MDPara *p1; // List
+    MDPara *p2; // end
+    MDNode *li0;
+    MDNode *li1;
+    MDNode *li0text;
+    MDNode *li1text;
+    MDNode *li1text_bold;
+    
+    CX_TEST_DO {
+        doc = parse_markdown(CX_STR("Test Paragraph\n\n - List1\n - List2 __bold__\n\nend"));
+        CX_TEST_ASSERT(doc);
+        
+        // paragraph structure
+        CX_TEST_ASSERT(doc->content);
+        p0 = doc->content;
+        CX_TEST_ASSERT(p0->type == MD_BLOCK_P);
+        
+        CX_TEST_ASSERT(p0->next);
+        p1 = p0->next;
+        CX_TEST_ASSERT(p1->type = MD_BLOCK_UL);
+        
+        CX_TEST_ASSERT(p1->next);
+        p2 = p1->next;
+        CX_TEST_ASSERT(p2->type == MD_BLOCK_P);
+        
+        // check list
+        li0 = p1->content;
+        CX_TEST_ASSERT(li0);
+        li1 = li0->next;
+        CX_TEST_ASSERT(li1);
+        
+        li0text = li0->children_begin;
+        CX_TEST_ASSERT(li0text);
+        CX_TEST_ASSERT(li0text->text.ptr != NULL);
+        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(li0text), CX_STR("List1")));
+        
+        li1text = li1->children_begin;
+        CX_TEST_ASSERT(li1text);
+        CX_TEST_ASSERT(li1text->text.ptr != NULL);
+        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(li1text), CX_STR("List2 ")));
+        li1text_bold = li1text->next;
+        CX_TEST_ASSERT(li1text_bold);
+        CX_TEST_ASSERT(li1text_bold->type == MD_SPAN_STRONG);
+        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(li1text_bold), CX_STR("bold")));
+        
+        // check end paragraph
+        CX_TEST_ASSERT(p2->content != NULL);
+        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(p2->content), CX_STR("end")));
+    }
+}
+
 static int str_eq(const char *s1, const char *s2) {
     if(!s1) {
         return s1 == s2;
index 6894146d704d1ac259da85af20d1cdc975ee2302..c0494513759aa060ecd295a333f93d8cf5c2b00e 100644 (file)
@@ -38,6 +38,7 @@ extern "C" {
 CX_TEST(test_parse_markdown_para);
 CX_TEST(test_parse_markdown_formatting_simple);
 CX_TEST(test_parse_markdown_formatting_nested);
+CX_TEST(test_parse_markdown_list);
 CX_TEST(test_mddoc_linearization);
 
 #ifdef __cplusplus
index 67a05c296fd8ee682baab42e962a452eb54015f3..f65c9079887e39977ba2dcf0b7fa0a5edf2bb4cc 100644 (file)
@@ -36,6 +36,7 @@ int main(int argc, char **argv) {
     cx_test_register(suite, test_parse_markdown_para);
     cx_test_register(suite, test_parse_markdown_formatting_simple);
     cx_test_register(suite, test_parse_markdown_formatting_nested);
+    cx_test_register(suite, test_parse_markdown_list);
     cx_test_register(suite, test_mddoc_linearization);
     
     cx_test_run_stdout(suite);