fix: hemar: section whitespaces checkpoint

This commit is contained in:
2025-05-19 11:07:10 +00:00
parent ef314495ba
commit 6c953563d0
4 changed files with 414 additions and 5 deletions

View File

@@ -736,6 +736,203 @@ template_parse_execute(MemoryContext context, const char **s_ptr,
return node;
}
/* Helper function to find the end of a tag */
static const char *
find_tag_end(const char *tag_start, const TemplateConfig *config)
{
const char *p = tag_start;
bool in_quotes = false;
char quote_char = 0;
/* Skip opening braces */
p += strlen(config->Syntax.Braces.open);
/* Skip whitespace after opening braces */
while (*p && isspace((unsigned char)*p))
p++;
/* Skip the tag keyword ("for" or "end") */
if (strncmp(p, config->Syntax.Section.control, strlen(config->Syntax.Section.control)) == 0)
{
p += strlen(config->Syntax.Section.control);
}
else if (strncmp(p, "end", 3) == 0)
{
p += 3;
}
else
{
return NULL; /* Not a control tag */
}
/* Skip whitespace after keyword */
while (*p && isspace((unsigned char)*p))
p++;
/* For "for" tags, skip the iterator and "in" parts */
if (strncmp(tag_start + strlen(config->Syntax.Braces.open), config->Syntax.Section.control,
strlen(config->Syntax.Section.control)) == 0)
{
/* Skip iterator name */
while (*p && !isspace((unsigned char)*p))
p++;
/* Skip whitespace */
while (*p && isspace((unsigned char)*p))
p++;
/* Skip "in" keyword */
if (strncmp(p, config->Syntax.Section.source, strlen(config->Syntax.Section.source)) == 0)
{
p += strlen(config->Syntax.Section.source);
/* Skip whitespace after "in" */
while (*p && isspace((unsigned char)*p))
p++;
/* Skip collection name */
while (*p && !isspace((unsigned char)*p))
{
if (*p == '"' || *p == '\'')
{
if (!in_quotes)
{
in_quotes = true;
quote_char = *p;
}
else if (*p == quote_char)
{
in_quotes = false;
}
}
p++;
}
}
}
/* Skip trailing whitespace */
while (*p && isspace((unsigned char)*p))
p++;
/* Find closing braces */
if (strncmp(p, config->Syntax.Braces.close, strlen(config->Syntax.Braces.close)) == 0)
{
return p + strlen(config->Syntax.Braces.close);
}
return NULL; /* Invalid tag format */
}
/* Helper function to check if a line contains only whitespace and a control tag */
static bool
is_control_tag_only_line(const char *line_start, const char *line_end, const char *tag_start, const char *tag_end)
{
const char *p;
/* Check whitespace before tag */
for (p = line_start; p < tag_start; p++)
{
if (!isspace((unsigned char)*p))
return false;
}
/* Check whitespace after tag */
for (p = tag_end; p < line_end; p++)
{
if (!isspace((unsigned char)*p))
return false;
}
return true;
}
/* Helper function to find the start of the current line */
static const char *
find_line_start(const char *text, const char *current_pos)
{
const char *p = current_pos;
/* Move backwards until we find a newline or the start of text */
while (p > text && *(p-1) != '\n')
p--;
return p;
}
/* Helper function to find the end of the current line */
static const char *
find_line_end(const char *text)
{
const char *p = text;
/* Move forwards until we find a newline or the end of text */
while (*p && *p != '\n')
p++;
return p;
}
/* Helper function to check if a tag is a control tag */
static bool
is_control_tag(const char *tag_start, const TemplateConfig *config)
{
const char *p = tag_start;
/* Skip opening braces and whitespace */
p += strlen(config->Syntax.Braces.open);
while (*p && isspace((unsigned char)*p))
p++;
/* Check for "for" or "end" */
return (strncmp(p, config->Syntax.Section.control, strlen(config->Syntax.Section.control)) == 0 ||
strncmp(p, "end", 3) == 0);
}
/* Helper function to trim whitespace around control tags */
static void
trim_control_tag_whitespace(const char **s_ptr, const TemplateConfig *config)
{
const char **s = s_ptr;
const char *line_start, *line_end, *tag_start, *tag_end;
/* Find the start of the current line */
line_start = find_line_start(*s - 100, *s); /* Look back up to 100 chars for line start */
if (line_start < *s - 100)
line_start = *s; /* If we couldn't find line start, use current position */
/* Find the end of the current line */
line_end = find_line_end(*s);
/* Find the tag boundaries */
tag_start = *s;
tag_end = find_tag_end(tag_start, config);
if (!tag_end)
return; /* Not a valid control tag */
/* Check if this is a control tag on its own line */
if (is_control_tag_only_line(line_start, line_end, tag_start, tag_end))
{
/* For opening tags, remove whitespace and newline after the tag */
if (strncmp(tag_start + strlen(config->Syntax.Braces.open), config->Syntax.Section.control,
strlen(config->Syntax.Section.control)) == 0)
{
/* Skip to the end of the line */
while (*tag_end && *tag_end != '\n')
tag_end++;
if (*tag_end == '\n')
tag_end++; /* Skip the newline */
*s = tag_end;
}
/* For closing tags, remove whitespace and newline before the tag */
else if (strncmp(tag_start + strlen(config->Syntax.Braces.open), "end", 3) == 0)
{
/* Move back to start of line */
*s = line_start;
}
}
}
/* Main template parser function */
TemplateNode *
template_parse(MemoryContext context, const char **s, const TemplateConfig *config,
@@ -769,7 +966,7 @@ template_parse(MemoryContext context, const char **s, const TemplateConfig *conf
while (*s && **s != '\0')
{
/* Check for closing brace in inner parse */
if (inner_parse && strncmp(*s, config->Syntax.Braces.close, strlen(config->Syntax.Braces.close)) == 0)
if (inner_parse && strncmp(*s, config->Syntax.Braces.open, strlen(config->Syntax.Braces.open)) == 0)
{
break;
}
@@ -796,6 +993,9 @@ template_parse(MemoryContext context, const char **s, const TemplateConfig *conf
current_node_filled = true;
}
/* Check for control tag and trim whitespace if needed */
trim_control_tag_whitespace(s, config);
/* Parse the tag */
tag_node = NULL;
tag_prefix = *s + strlen(config->Syntax.Braces.open);