From 9b817fe25a96e8d53775c6245e33b541573639e2 Mon Sep 17 00:00:00 2001 From: defname Date: Thu, 23 Oct 2025 16:06:00 +0200 Subject: [PATCH 1/6] add const to parameter if function does not modify it. add Buffer_Has_Space(), Buffer_Clear() --- src/common/buffer.c | 17 +++++++++++++---- src/common/buffer.h | 16 ++++++++++++---- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/common/buffer.c b/src/common/buffer.c index 4140631..5cfc1ee 100644 --- a/src/common/buffer.c +++ b/src/common/buffer.c @@ -39,6 +39,11 @@ void Buffer_Deinit(Buffer *buffer) { buffer->buffer = NULL; } +void Buffer_Clear(Buffer *buffer) { + buffer->count = 0; + buffer->cursor = 0; +} + void Buffer_Enqueue(Buffer *buffer, const void *element) { if (buffer->count >= buffer->capacity) { logError("Buffer overflow. Element dropped."); @@ -65,19 +70,23 @@ bool Buffer_Dequeue(Buffer *buffer, void *out_element) { return true; } -bool Buffer_IsEmpty(Buffer *buffer) { +bool Buffer_IsEmpty(const Buffer *buffer) { return buffer->count == 0; } -size_t Buffer_Size(Buffer *buffer) { +size_t Buffer_Size(const Buffer *buffer) { return buffer->count; } -size_t Buffer_Capacity(Buffer *buffer) { +size_t Buffer_Capacity(const Buffer *buffer) { return buffer->capacity; } -bool Buffer_Peek(Buffer *buffer, size_t lookahead, void *out_element) { +bool Buffer_HasSpace(const Buffer *buffer) { + return buffer->count < buffer->capacity - 1; +} + +bool Buffer_Peek(const Buffer *buffer, size_t lookahead, void *out_element) { if (lookahead >= buffer->count) { logError("Buffer underflow on Peek (lookahead: %zu, count: %zu).", lookahead, buffer->count); return false; diff --git a/src/common/buffer.h b/src/common/buffer.h index 6f8421a..a8f3973 100644 --- a/src/common/buffer.h +++ b/src/common/buffer.h @@ -48,6 +48,11 @@ void Buffer_Init(Buffer *buffer, size_t capacity, size_t element_size); */ void Buffer_Deinit(Buffer *buffer); +/** + * @brief Set buffer count to 0 and cursor to the first element. + */ +void Buffer_Clear(Buffer *buffer); + /** * @brief Adds an element to the end (tail) of the buffer. * If the buffer is full, the element is dropped. @@ -70,13 +75,16 @@ bool Buffer_Dequeue(Buffer *buffer, void *out_element); * @param buffer A pointer to the Buffer struct. * @return true if the buffer contains no elements, false otherwise. */ -bool Buffer_IsEmpty(Buffer *buffer); +bool Buffer_IsEmpty(const Buffer *buffer); /** @brief Returns the current number of elements in the buffer. */ -size_t Buffer_Size(Buffer *buffer); +size_t Buffer_Size(const Buffer *buffer); /** @brief Returns the maximum capacity of the buffer. */ -size_t Buffer_Capacity(Buffer *buffer); +size_t Buffer_Capacity(const Buffer *buffer); + +/** @brief Return true if there are empty slots. */ +bool Buffer_HasSpace(const Buffer *buffer); /** * @brief Peeks at an element in the buffer without removing it. @@ -85,6 +93,6 @@ size_t Buffer_Capacity(Buffer *buffer); * @param out_element A pointer to a variable where the peeked element will be copied. * @return true on success, false if the lookahead index is out of bounds. */ -bool Buffer_Peek(Buffer *buffer, size_t lookahead, void *out_element); +bool Buffer_Peek(const Buffer *buffer, size_t lookahead, void *out_element); #endif \ No newline at end of file From 37801ecd91a77794aea853934f614178416f7c86 Mon Sep 17 00:00:00 2001 From: defname Date: Thu, 23 Oct 2025 17:53:19 +0200 Subject: [PATCH 2/6] cache regex results for better performance --- src/syntax/definition.h | 13 +++++++++ src/syntax/highlighting.c | 58 ++++++++++++++++++++++++++++++--------- 2 files changed, 58 insertions(+), 13 deletions(-) diff --git a/src/syntax/definition.h b/src/syntax/definition.h index a5c343b..d93103d 100644 --- a/src/syntax/definition.h +++ b/src/syntax/definition.h @@ -99,6 +99,16 @@ typedef struct _SyntaxDefinitionError { void SyntaxDefinitionError_Deinit(SyntaxDefinitionError *error); +/** + * @brief Helper struct to cache regex match results. This information are used by the Highlight module. + */ +typedef struct _MatchCache { + ssize_t offset; // total offset from where match was calculated + regmatch_t match; // last match + bool done; // if true the last match was already found +} MatchCache; + + /** * @brief Holds the definition of a syntax block. */ @@ -113,6 +123,9 @@ typedef struct _SyntaxBlockDef { size_t children_count; //< number of children uint8_t color; //< the color to render the block + + MatchCache start_cache; //< used by the Highlight module + MatchCache end_cache; //< used by the Highlight module } SyntaxBlockDef; SyntaxBlockDef *SyntaxBlockDef_Create(); diff --git a/src/syntax/highlighting.c b/src/syntax/highlighting.c index 6d75c63..2568357 100644 --- a/src/syntax/highlighting.c +++ b/src/syntax/highlighting.c @@ -99,14 +99,33 @@ void SyntaxHighlighting_Deinit(SyntaxHighlighting *hl) { hl->def = NULL; } -static const SyntaxBlockDef *find_first_child(const char *str, const SyntaxBlockDef *current, regmatch_t *match) { +static bool regexec_with_cache(const regex_t *regex, const char *str, size_t offset, MatchCache *cache, regmatch_t *match) { + if (cache->done) { + return false; + } + if (cache->match.rm_so + cache->offset >= (regoff_t)offset) { + *match = cache->match; + match->rm_so = cache->match.rm_so + cache->offset - offset; + match->rm_eo = cache->match.rm_eo + cache->offset - offset; + return true; + } + if (regexec(regex, str + offset, 1, match, 0) == 0) { + cache->match = *match; + cache->offset = offset; + return true; + } + cache->done = true; + return false; +} + +static SyntaxBlockDef *find_first_child(const char *str, size_t offset, SyntaxBlockDef *current, regmatch_t *match) { bool had_match = false; regmatch_t first_match; - const SyntaxBlockDef *first_block; + SyntaxBlockDef *first_block = NULL; for (size_t i=0; ichildren_count; i++) { - const SyntaxBlockDef *child = current->children[i]; + SyntaxBlockDef *child = current->children[i]; regmatch_t curr_match; - if (regexec(&child->start, str, 1, &curr_match, 0) == 0) { + if (regexec_with_cache(&child->start, str, offset, &child->start_cache, &curr_match)) { if (!had_match || curr_match.rm_so < first_match.rm_so) { first_match = curr_match; first_block = child; @@ -121,13 +140,23 @@ static const SyntaxBlockDef *find_first_child(const char *str, const SyntaxBlock return NULL; } -static bool find_end_of_block(const char *str, const SyntaxBlockDef *current, regmatch_t *match) { - regmatch_t end_match; - if (regexec(¤t->end, str, 1, &end_match, 0) == 0) { - *match = end_match; - return true; +static bool find_end_of_block(const char *str, size_t offset, SyntaxBlockDef *current, regmatch_t *match) { + return regexec_with_cache(¤t->end, str, offset, ¤t->end_cache, match); +} + +static void init_match_cache(SyntaxHighlighting *sh) { + for (size_t i=0; idef->blocks_count; i++) { + SyntaxBlockDef *block = sh->def->blocks[i]; + block->start_cache.match.rm_so = -1; + block->start_cache.match.rm_eo = -1; + block->start_cache.offset = 0; + block->start_cache.done = false; + block->end_cache.match.rm_so = -1; + block->end_cache.match.rm_eo = -1; + block->end_cache.offset = 0; + block->end_cache.done = false; + } - return false; } Stack *SyntaxHighlighting_HighlightString(SyntaxHighlighting *sh, const String *text, const Stack *open_blocks_at_begin) { @@ -153,17 +182,20 @@ Stack *SyntaxHighlighting_HighlightString(SyntaxHighlighting *sh, const String * Stack_Push(open_blocks, sh->def->root); } + // initialize the cache fields of the SyntaxBlockDefs + init_match_cache(sh); + // iterate over the string size_t offset = 0; for (;;) { // take the current block from the stack (but keep it there) - const SyntaxBlockDef *current_block = (SyntaxBlockDef*)Stack_Peek(open_blocks); + SyntaxBlockDef *current_block = (SyntaxBlockDef*)Stack_Peek(open_blocks); // find the first child block regmatch_t child_match; - const SyntaxBlockDef *child = find_first_child(text->bytes + offset, current_block, &child_match); + SyntaxBlockDef *child = find_first_child(text->bytes, offset, current_block, &child_match); regmatch_t end_match; - bool end_found = find_end_of_block(text->bytes + offset, current_block, &end_match); + bool end_found = find_end_of_block(text->bytes, offset, current_block, &end_match); if (child && (!end_found || child_match.rm_so < end_match.rm_so)) { // if there is a child block found create and add a tag to SyntaxHighlightingString tag list From 713510c12057927d8287a0c756709cf4b74bc9a4 Mon Sep 17 00:00:00 2001 From: defname Date: Thu, 23 Oct 2025 18:52:03 +0200 Subject: [PATCH 3/6] add "ends_on" (INI) property to SyntaxBlockDef --- src/syntax/definition.c | 142 ++++++++++++++++++++++----------- src/syntax/definition.h | 5 +- tests/test_syntax_definition.c | 35 +++++++- 3 files changed, 131 insertions(+), 51 deletions(-) diff --git a/src/syntax/definition.c b/src/syntax/definition.c index f8e23a4..82a3c6d 100644 --- a/src/syntax/definition.c +++ b/src/syntax/definition.c @@ -47,6 +47,8 @@ SyntaxBlockDef *SyntaxBlockDef_Create() { block->only_start = false; block->children = NULL; block->children_count = 0; + block->ends_on = NULL; + block->ends_on_count = 0; return block; } @@ -55,6 +57,9 @@ void SyntaxBlockDef_Destroy(SyntaxBlockDef *block) { if (block->children) { free(block->children); } + if (block->ends_on) { + free(block->ends_on); + } if (block->name) { free(block->name); } @@ -212,6 +217,68 @@ static SyntaxDefinitionError build_blocks(SyntaxDefinition *def, const Table *ta return NO_ERROR; } +static SyntaxDefinitionError map_block_names_to_blocks(SyntaxBlockDef *current_block, StringView *block_names, size_t count, SyntaxBlockDef **out, Table *blocks) { + for (size_t i=0; iname, name.bytes + ); + String_Deinit(&name); + return error; + } + String_Deinit(&name); + out[i] = mapping->block; + } + return NO_ERROR; +} + +static SyntaxDefinitionError block_name_list_str_to_blocks(SyntaxBlockDef *current_block, const char *list_str, SyntaxBlockDef ***out, size_t *out_count, Table *blocks) { + if (!list_str) { + *out_count = 0; + return NO_ERROR; + } + + String s = String_FromCStr(list_str, strlen(list_str)); + ssize_t count = 0; + String delimiter = String_FromCStr(",", 1); + StringView *children = String_Split(&s, &delimiter, &count); + if (count < 0) { + logFatal("Potential error in String_Split()."); + } + String_Deinit(&delimiter); + if (count == 0) { + String_Deinit(&s); + free(children); + return NO_ERROR; + } + + // malloc memory for the children of SyntaxBlockDef + *out = malloc(sizeof(SyntaxBlockDef*) * count); + if (!(*out)) { + logFatal("Cannot allocate memory for children of SyntaxBlockDef."); + } + + SyntaxDefinitionError error = map_block_names_to_blocks(current_block, children, count, *out, blocks); + if (error.code != SYNTAXDEFINITION_NO_ERROR) { + String_Deinit(&s); + free(children); + return error; + } + + *out_count = count; + String_Deinit(&s); + free(children); + + return NO_ERROR; +} SyntaxDefinitionError link_children(SyntaxDefinition *def, Table *blocks) { for (size_t i=0; iblocks_count; i++) { @@ -224,55 +291,31 @@ SyntaxDefinitionError link_children(SyntaxDefinition *def, Table *blocks) { } const Table *block_table = mapping->table; - const char *children_str = TypedTable_GetString(block_table, "allowed_blocks"); - if (!children_str) { - continue; - } - - String s = String_FromCStr(children_str, strlen(children_str)); - ssize_t children_count = 0; - String delimiter = String_FromCStr(",", 1); - StringView *children = String_Split(&s, &delimiter, &children_count); - if (children_count < 0) { - logFatal("Potential rrror in String_Split()."); - } - String_Deinit(&delimiter); - if (children_count == 0) { - String_Deinit(&s); - free(children); - continue; + const char *children_str = TypedTable_GetString(block_table, "child_blocks"); + SyntaxDefinitionError error = block_name_list_str_to_blocks(block, children_str, &block->children, &block->children_count, blocks); + if (error.code != SYNTAXDEFINITION_NO_ERROR) { + return error; } + } + return NO_ERROR; +} - // malloc memory for the children of SyntaxBlockDef - block->children = malloc(sizeof(SyntaxBlockDef*) * children_count); - if (!block->children) { - logFatal("Cannot allocate memory for children of SyntaxBlockDef."); +SyntaxDefinitionError link_ends_with(SyntaxDefinition *def, Table *blocks) { + for (size_t i=0; iblocks_count; i++) { + SyntaxBlockDef *block = def->blocks[i]; + + // get the mapping + table_block_mapping *mapping = Table_Get(blocks, block->name); + if (!mapping) { + logFatal("Some serious design flaw detected."); } + const Table *block_table = mapping->table; - for (size_t child_idx=0; child_idx<(size_t)children_count; child_idx++) { - String child_name = String_FromView(children[child_idx]); - String_Trim(&child_name); - table_block_mapping *child_mapping = Table_Get(blocks, child_name.bytes); - - if (!child_mapping) { - // no block with the given child name - // something like - // allowed_blocks = block1, non_existing_block - SyntaxDefinitionError error = ERROR( - SYNTAXDEFINITION_BLOCK_DOES_NOT_EXIST, - "Block \"%s\" defines non-existing block \"%s\" as it's child.", block->name, child_name.bytes - ); - String_Deinit(&child_name); - String_Deinit(&s); - free(children); - return error; - } - String_Deinit(&child_name); - block->children[child_idx] = child_mapping->block; + const char *ends_with_str = TypedTable_GetString(block_table, "ends_on"); + SyntaxDefinitionError error = block_name_list_str_to_blocks(block, ends_with_str, &block->ends_on, &block->ends_on_count, blocks); + if (error.code != SYNTAXDEFINITION_NO_ERROR) { + return error; } - block->children_count = children_count; - String_Deinit(&s); - free(children); } return NO_ERROR; } @@ -294,7 +337,6 @@ SyntaxDefinition *SyntaxDefinition_FromTable(const Table *table, SyntaxDefinitio SyntaxDefinition_Destroy(def); return NULL; } - SyntaxDefinitionError_Deinit(error); // temporary table mapping all block names to it's SyntaxBlockDef Table *blocks = Table_Create(); @@ -304,7 +346,6 @@ SyntaxDefinition *SyntaxDefinition_FromTable(const Table *table, SyntaxDefinitio Table_Destroy(blocks); return NULL; } - SyntaxDefinitionError_Deinit(error); // construct the children lists *error = link_children(def, blocks); @@ -313,7 +354,14 @@ SyntaxDefinition *SyntaxDefinition_FromTable(const Table *table, SyntaxDefinitio Table_Destroy(blocks); return NULL; } - SyntaxDefinitionError_Deinit(error); + + // construct the ends_by list + *error = link_ends_with(def, blocks); + if (error->code != SYNTAXDEFINITION_NO_ERROR) { + SyntaxDefinition_Destroy(def); + Table_Destroy(blocks); + return NULL; + } // cleanup Table_Destroy(blocks); diff --git a/src/syntax/definition.h b/src/syntax/definition.h index d93103d..f174914 100644 --- a/src/syntax/definition.h +++ b/src/syntax/definition.h @@ -38,7 +38,7 @@ * name = TEST * * [block:root] - * allowed_blocks = string, keyword + * child_blocks = string, keyword * * [block:string] * start = "'" @@ -121,6 +121,9 @@ typedef struct _SyntaxBlockDef { struct _SyntaxBlockDef **children; //< list of children definition which are allowed inside the block size_t children_count; //< number of children + + struct _SyntaxBlockDef **ends_on; //< list of blocks which may end this block implicitly + size_t ends_on_count; //< number of blocks which may end this block implicitly uint8_t color; //< the color to render the block diff --git a/tests/test_syntax_definition.c b/tests/test_syntax_definition.c index 58d8250..a7ea232 100644 --- a/tests/test_syntax_definition.c +++ b/tests/test_syntax_definition.c @@ -158,7 +158,7 @@ void test_errors(void) { "name = MINI\n" "[block:root]\n" "start=.*\n" - "allowed_blocks=foo", + "child_blocks=foo", SYNTAXDEFINITION_BLOCK_DOES_NOT_EXIST }, { @@ -166,7 +166,7 @@ void test_errors(void) { "name = MINI\n" "[block:root]\n" "start=.*\n" - "allowed_blocks=foo, bar\n" + "child_blocks=foo, bar\n" "[block:foo]\n" "start=.*\n", SYNTAXDEFINITION_BLOCK_DOES_NOT_EXIST @@ -190,7 +190,7 @@ void test_children(void) { "name = Children\n" "[block:root]\n" "start=\".\"\n" - "allowed_blocks=block1,block2\n" + "child_blocks=block1,block2\n" "[block:block1]\n" "start=\".\"\n" "[block:block2]\n" @@ -208,6 +208,34 @@ void test_children(void) { SyntaxDefinitionError_Deinit(&error); } +void test_ends_on(void) { + const char *ini = + "[meta]\n" + "name = EndsOn\n" + "[block:root]\n" + "child_blocks=block1\n" + "[block:block1]\n" + "start=\".\"\n" + "ends_on=block3,block2\n" + "[block:block2]\n" + "start=\".\"\n" + "[block:block3]\n" + "start=\".\"\n"; + Table *table = table_from_ini(ini); + SyntaxDefinitionError error; + SyntaxDefinition *def = SyntaxDefinition_FromTable(table, &error); + TEST_ASSERT(def != NULL); + TEST_CHECK(def->blocks_count == 4); + TEST_CHECK(def->root != NULL); + SyntaxBlockDef *block1 = def->root->children[0]; + TEST_CHECK(block1 != NULL); + TEST_CHECK(block1->ends_on_count == 2); + TEST_MSG("%zu", block1->ends_on_count); + + Table_Destroy(table); + SyntaxDefinition_Destroy(def); + SyntaxDefinitionError_Deinit(&error); +} TEST_LIST = { { "SyntaxDefinition: Block Simple", test_block_simple }, @@ -216,5 +244,6 @@ TEST_LIST = { { "SyntaxDefinition: Minimal Example", test_minimal_definition }, { "SyntaxDefinition: Errors", test_errors }, { "SyntaxDefinition: Children", test_children }, + { "SyntaxDefinition: Ends On", test_ends_on}, { NULL, NULL } }; From 224654f3b2500a45eb97bacdc1b6fea724b7a39f Mon Sep 17 00:00:00 2001 From: defname Date: Thu, 23 Oct 2025 19:39:45 +0200 Subject: [PATCH 4/6] implement ends_on in the SyntaxHighlight modul --- src/syntax/highlighting.c | 74 +++++++++++++++++++++++++++----- tests/test_syntax_highlighting.c | 6 +-- 2 files changed, 66 insertions(+), 14 deletions(-) diff --git a/src/syntax/highlighting.c b/src/syntax/highlighting.c index 2568357..38ffa92 100644 --- a/src/syntax/highlighting.c +++ b/src/syntax/highlighting.c @@ -118,17 +118,17 @@ static bool regexec_with_cache(const regex_t *regex, const char *str, size_t off return false; } -static SyntaxBlockDef *find_first_child(const char *str, size_t offset, SyntaxBlockDef *current, regmatch_t *match) { +static SyntaxBlockDef *find_first_block(SyntaxBlockDef **block_list, size_t block_list_count, const char *str, size_t offset, regmatch_t *match) { bool had_match = false; - regmatch_t first_match; + regmatch_t first_match = {0}; SyntaxBlockDef *first_block = NULL; - for (size_t i=0; ichildren_count; i++) { - SyntaxBlockDef *child = current->children[i]; + for (size_t i=0; istart, str, offset, &child->start_cache, &curr_match)) { + if (regexec_with_cache(&curr->start, str, offset, &curr->start_cache, &curr_match)) { if (!had_match || curr_match.rm_so < first_match.rm_so) { first_match = curr_match; - first_block = child; + first_block = curr; had_match = true; } } @@ -140,10 +140,19 @@ static SyntaxBlockDef *find_first_child(const char *str, size_t offset, SyntaxBl return NULL; } +static SyntaxBlockDef *find_first_child(const char *str, size_t offset, SyntaxBlockDef *current, regmatch_t *match) { + return find_first_block(current->children, current->children_count, str, offset, match); +} + +static SyntaxBlockDef *find_first_ends_on_block(const char *str, size_t offset, SyntaxBlockDef *current, regmatch_t *match) { + return find_first_block(current->ends_on, current->ends_on_count, str, offset, match); +} + static bool find_end_of_block(const char *str, size_t offset, SyntaxBlockDef *current, regmatch_t *match) { return regexec_with_cache(¤t->end, str, offset, ¤t->end_cache, match); } + static void init_match_cache(SyntaxHighlighting *sh) { for (size_t i=0; idef->blocks_count; i++) { SyntaxBlockDef *block = sh->def->blocks[i]; @@ -194,10 +203,22 @@ Stack *SyntaxHighlighting_HighlightString(SyntaxHighlighting *sh, const String * // find the first child block regmatch_t child_match; SyntaxBlockDef *child = find_first_child(text->bytes, offset, current_block, &child_match); - regmatch_t end_match; - bool end_found = find_end_of_block(text->bytes, offset, current_block, &end_match); + // find the first ends_on block + regmatch_t ends_on_match; + SyntaxBlockDef *ends_on = find_first_ends_on_block(text->bytes, offset, current_block, &ends_on_match); - if (child && (!end_found || child_match.rm_so < end_match.rm_so)) { + // find the block_end + regmatch_t end_match = {0, 0}; // current position with no consumption + bool end_found = true; // true for the case current_block is an only start block + if (!current_block->only_start) { + end_found = find_end_of_block(text->bytes, offset, current_block, &end_match); + } + + // check if child is the first match + if (child + && (!ends_on || child_match.rm_so < ends_on_match.rm_so) + && (!end_found || child_match.rm_so < end_match.rm_so)) + { // if there is a child block found create and add a tag to SyntaxHighlightingString tag list SyntaxHighlightingTag tag; tag.text = text; @@ -212,8 +233,39 @@ Stack *SyntaxHighlighting_HighlightString(SyntaxHighlighting *sh, const String * Stack_Push(open_blocks, (void*)child); continue; } - // no child block found - // so check if the last block ends + // check if ends_on is the first match + if (ends_on + && (!end_found || ends_on_match.rm_so < end_match.rm_so)) + { + // the current block ends by the occurence of the ends_on block + Stack_Pop(open_blocks); // removes current (which was just peeked before) + + // check if the ends_on block is allowed in the surrounding block + SyntaxBlockDef *surrounding_block = Stack_Peek(open_blocks); + bool is_valid = false; + for (size_t i=0; ichildren_count; i++) { + if (surrounding_block->children[i] == ends_on) { + is_valid = true; + break; + } + } + if (!is_valid) { + // if the ending block is not valid put the current block back on the stack + Stack_Push(open_blocks, current_block); + // and ignore what just happend + continue; + } + SyntaxHighlightingTag tag; + tag.text = text; + tag.byte_offset = offset + ends_on_match.rm_so; + tag.block = surrounding_block; + SyntaxHighlightingString_AddTag(shs, tag); + + // increase the offset to the end of the match + offset += ends_on_match.rm_eo; + continue; + } + // last case.... if end_found it's the first match automatically if (end_found) { // end of block found so remove it from stack Stack_Pop(open_blocks); // removes current (which was just peeked before) diff --git a/tests/test_syntax_highlighting.c b/tests/test_syntax_highlighting.c index c73406e..1b53e76 100644 --- a/tests/test_syntax_highlighting.c +++ b/tests/test_syntax_highlighting.c @@ -96,7 +96,7 @@ const char *test_ini0 = "[block:root]\n" "start=^\n" "end=a^\n" -"allowed_blocks=string\n" +"child_blocks=string\n" "\n" "[block:string]\n" "start=\"'\"\n" @@ -134,12 +134,12 @@ const char *test_ini1 = R"( name = TEST [block:root] -allowed_blocks=string, comment, keyword, brackets +child_blocks=string, comment, keyword, brackets [block:brackets] start=\( end=\) -allowed_blocks=string, keyword, brackets +child_blocks=string, keyword, brackets [block:keyword] start=keyword From 6edad871a41aa95773cfaf0f6ad542c1282c6d94 Mon Sep 17 00:00:00 2001 From: defname Date: Thu, 23 Oct 2025 20:18:57 +0200 Subject: [PATCH 5/6] remove the check if the ends_on block is allowed inside the surrounding block. this leads to more flexibility and better performance --- src/syntax/highlighting.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/syntax/highlighting.c b/src/syntax/highlighting.c index 38ffa92..837523a 100644 --- a/src/syntax/highlighting.c +++ b/src/syntax/highlighting.c @@ -240,27 +240,16 @@ Stack *SyntaxHighlighting_HighlightString(SyntaxHighlighting *sh, const String * // the current block ends by the occurence of the ends_on block Stack_Pop(open_blocks); // removes current (which was just peeked before) - // check if the ends_on block is allowed in the surrounding block - SyntaxBlockDef *surrounding_block = Stack_Peek(open_blocks); - bool is_valid = false; - for (size_t i=0; ichildren_count; i++) { - if (surrounding_block->children[i] == ends_on) { - is_valid = true; - break; - } - } - if (!is_valid) { - // if the ending block is not valid put the current block back on the stack - Stack_Push(open_blocks, current_block); - // and ignore what just happend - continue; - } + // add a tag for the beginning of the ends_on block SyntaxHighlightingTag tag; tag.text = text; tag.byte_offset = offset + ends_on_match.rm_so; - tag.block = surrounding_block; + tag.block = ends_on; SyntaxHighlightingString_AddTag(shs, tag); + // add the ends_on block to the stack + Stack_Push(open_blocks, (void*)ends_on); + // increase the offset to the end of the match offset += ends_on_match.rm_eo; continue; From d5f3f1bd331b7aa3c24d152b44d608957dccfef8 Mon Sep 17 00:00:00 2001 From: defname Date: Thu, 23 Oct 2025 20:19:18 +0200 Subject: [PATCH 6/6] add additional (but just a single) test for the new feature --- tests/test_syntax_highlighting.c | 44 ++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/test_syntax_highlighting.c b/tests/test_syntax_highlighting.c index 1b53e76..54ecfe6 100644 --- a/tests/test_syntax_highlighting.c +++ b/tests/test_syntax_highlighting.c @@ -153,6 +153,7 @@ start = // end = $ )"; + void test_basics(void) { TagTestCase cases[] = { { @@ -245,7 +246,49 @@ void test_basics(void) { 1, {"root"} }, + }; + size_t count = sizeof(cases) / sizeof(cases[0]); + + for (size_t i=0; i