summaryrefslogtreecommitdiff
path: root/src/blocks.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/blocks.c')
-rw-r--r--src/blocks.c106
1 files changed, 78 insertions, 28 deletions
diff --git a/src/blocks.c b/src/blocks.c
index 7f58ffd..804ad82 100644
--- a/src/blocks.c
+++ b/src/blocks.c
@@ -34,6 +34,10 @@ static bool S_last_line_blank(const cmark_node *node) {
return (node->flags & CMARK_NODE__LAST_LINE_BLANK) != 0;
}
+static bool S_last_line_checked(const cmark_node *node) {
+ return (node->flags & CMARK_NODE__LAST_LINE_CHECKED) != 0;
+}
+
static CMARK_INLINE cmark_node_type S_type(const cmark_node *node) {
return (cmark_node_type)node->type;
}
@@ -45,6 +49,10 @@ static void S_set_last_line_blank(cmark_node *node, bool is_blank) {
node->flags &= ~CMARK_NODE__LAST_LINE_BLANK;
}
+static void S_set_last_line_checked(cmark_node *node) {
+ node->flags |= CMARK_NODE__LAST_LINE_CHECKED;
+}
+
static CMARK_INLINE bool S_is_line_end_char(char c) {
return (c == '\n' || c == '\r');
}
@@ -97,6 +105,7 @@ cmark_parser *cmark_parser_new_with_mem(int options, cmark_mem *mem) {
parser->column = 0;
parser->first_nonspace = 0;
parser->first_nonspace_column = 0;
+ parser->thematic_break_kill_pos = 0;
parser->indent = 0;
parser->blank = false;
parser->partially_consumed_tab = false;
@@ -207,19 +216,17 @@ static void remove_trailing_blank_lines(cmark_strbuf *ln) {
// Check to see if a node ends with a blank line, descending
// if needed into lists and sublists.
-static bool ends_with_blank_line(cmark_node *node) {
- cmark_node *cur = node;
- while (cur != NULL) {
- if (S_last_line_blank(cur)) {
- return true;
- }
- if (S_type(cur) == CMARK_NODE_LIST || S_type(cur) == CMARK_NODE_ITEM) {
- cur = cur->last_child;
- } else {
- cur = NULL;
- }
+static bool S_ends_with_blank_line(cmark_node *node) {
+ if (S_last_line_checked(node)) {
+ return(S_last_line_blank(node));
+ } else if ((S_type(node) == CMARK_NODE_LIST ||
+ S_type(node) == CMARK_NODE_ITEM) && node->last_child) {
+ S_set_last_line_checked(node);
+ return(S_ends_with_blank_line(node->last_child));
+ } else {
+ S_set_last_line_checked(node);
+ return (S_last_line_blank(node));
}
- return false;
}
static cmark_node *finalize(cmark_parser *parser, cmark_node *b) {
@@ -316,7 +323,8 @@ static cmark_node *finalize(cmark_parser *parser, cmark_node *b) {
// spaces between them:
subitem = item->first_child;
while (subitem) {
- if (ends_with_blank_line(subitem) && (item->next || subitem->next)) {
+ if ((item->next || subitem->next) &&
+ S_ends_with_blank_line(subitem)) {
b->as.list.tight = false;
break;
}
@@ -608,6 +616,40 @@ static void chop_trailing_hashtags(cmark_chunk *ch) {
}
}
+// Check for thematic break. On failure, return 0 and update
+// thematic_break_kill_pos with the index at which the
+// parse fails. On success, return length of match.
+// "...three or more hyphens, asterisks,
+// or underscores on a line by themselves. If you wish, you may use
+// spaces between the hyphens or asterisks."
+static int S_scan_thematic_break(cmark_parser *parser, cmark_chunk *input,
+ bufsize_t offset) {
+ bufsize_t i;
+ char c;
+ char nextc = '\0';
+ int count;
+ i = offset;
+ c = peek_at(input, i);
+ if (!(c == '*' || c == '_' || c == '-')) {
+ parser->thematic_break_kill_pos = i;
+ return 0;
+ }
+ count = 1;
+ while ((nextc = peek_at(input, ++i))) {
+ if (nextc == c) {
+ count++;
+ } else if (nextc != ' ' && nextc != '\t') {
+ break;
+ }
+ }
+ if (count >= 3 && (nextc == '\r' || nextc == '\n')) {
+ return (i - offset) + 1;
+ } else {
+ parser->thematic_break_kill_pos = i;
+ return 0;
+ }
+}
+
// Find first nonspace character from current offset, setting
// parser->first_nonspace, parser->first_nonspace_column,
// parser->indent, and parser->blank. Does not advance parser->offset.
@@ -615,22 +657,24 @@ static void S_find_first_nonspace(cmark_parser *parser, cmark_chunk *input) {
char c;
int chars_to_tab = TAB_STOP - (parser->column % TAB_STOP);
- parser->first_nonspace = parser->offset;
- parser->first_nonspace_column = parser->column;
- while ((c = peek_at(input, parser->first_nonspace))) {
- if (c == ' ') {
- parser->first_nonspace += 1;
- parser->first_nonspace_column += 1;
- chars_to_tab = chars_to_tab - 1;
- if (chars_to_tab == 0) {
+ if (parser->first_nonspace <= parser->offset) {
+ parser->first_nonspace = parser->offset;
+ parser->first_nonspace_column = parser->column;
+ while ((c = peek_at(input, parser->first_nonspace))) {
+ if (c == ' ') {
+ parser->first_nonspace += 1;
+ parser->first_nonspace_column += 1;
+ chars_to_tab = chars_to_tab - 1;
+ if (chars_to_tab == 0) {
+ chars_to_tab = TAB_STOP;
+ }
+ } else if (c == '\t') {
+ parser->first_nonspace += 1;
+ parser->first_nonspace_column += chars_to_tab;
chars_to_tab = TAB_STOP;
+ } else {
+ break;
}
- } else if (c == '\t') {
- parser->first_nonspace += 1;
- parser->first_nonspace_column += chars_to_tab;
- chars_to_tab = TAB_STOP;
- } else {
- break;
}
}
@@ -939,12 +983,14 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
S_advance_offset(parser, input, input->len - 1 - parser->offset, false);
} else if (!indented &&
!(cont_type == CMARK_NODE_PARAGRAPH && !all_matched) &&
- (matched = scan_thematic_break(input, parser->first_nonspace))) {
+ (parser->thematic_break_kill_pos <= parser->first_nonspace) &&
+ (matched = S_scan_thematic_break(parser, input, parser->first_nonspace))) {
// it's only now that we know the line is not part of a setext heading:
*container = add_child(parser, *container, CMARK_NODE_THEMATIC_BREAK,
parser->first_nonspace + 1);
S_advance_offset(parser, input, input->len - 1 - parser->offset, false);
} else if ((!indented || cont_type == CMARK_NODE_LIST) &&
+ parser->indent < 4 &&
(matched = parse_list_marker(
parser->mem, input, parser->first_nonspace,
(*container)->type == CMARK_NODE_PARAGRAPH, &data))) {
@@ -1159,6 +1205,10 @@ static void S_process_line(cmark_parser *parser, const unsigned char *buffer,
parser->offset = 0;
parser->column = 0;
+ parser->first_nonspace = 0;
+ parser->first_nonspace_column = 0;
+ parser->thematic_break_kill_pos = 0;
+ parser->indent = 0;
parser->blank = false;
parser->partially_consumed_tab = false;