示例#1
0
文件: iterator.c 项目: lowks/cmark.ex
void cmark_consolidate_text_nodes(cmark_node *root)
{
	cmark_iter *iter = cmark_iter_new(root);
	cmark_strbuf buf = GH_BUF_INIT;
	cmark_event_type ev_type;
	cmark_node *cur, *tmp, *next;

	while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
		cur = cmark_iter_get_node(iter);
		if (ev_type == CMARK_EVENT_ENTER &&
		    cur->type == CMARK_NODE_TEXT &&
		    cur->next &&
		    cur->next->type == CMARK_NODE_TEXT) {
			cmark_strbuf_clear(&buf);
			cmark_strbuf_puts(&buf, cmark_node_get_literal(cur));
			tmp = cur->next;
			while (tmp && tmp->type == CMARK_NODE_TEXT) {
				cmark_iter_next(iter); // advance pointer
				cmark_strbuf_puts(&buf, cmark_node_get_literal(tmp));
				next = tmp->next;
				cmark_node_free(tmp);
				tmp = next;
			}
			cmark_node_set_literal(cur, (char *)cmark_strbuf_detach(&buf));
		}
	}

	cmark_iter_free(iter);
}
示例#2
0
static cmark_node *fixup_nodes(cmark_inline_parser *inline_parser,
                                  cmark_node *parent,
                                  int size)
{
  int node_text_len;
  cmark_node *prev = NULL;
  cmark_node *tmp;
  int name_size = size;
  cmark_strbuf *name;

  for (prev = cmark_node_last_child(parent); prev; prev = cmark_node_previous(prev)) {
    if (cmark_node_get_type(prev) == CMARK_NODE_TEXT) {
      const char *text = cmark_node_get_literal(prev);
      node_text_len = strlen(text);
      size -= node_text_len;

      if (size <= 0) {
        if (size < 0) {
          char *split_text = my_strndup(text, size * -1);
          cmark_node *split = cmark_node_new(CMARK_NODE_TEXT);

          cmark_node_set_literal(split, split_text);
          free(split_text);

          split_text = my_strndup(text + (size * - 1), node_text_len - size);
          cmark_node_set_literal(prev, split_text);
          free(split_text);

          cmark_node_insert_before(prev, split);
        }
        break;
      }
    } else {
      return NULL;
    }
  }

  name = cmark_strbuf_new(name_size + 1);

  tmp = prev;

  while (tmp) {
    cmark_node *next = cmark_node_next(tmp);

    cmark_strbuf_puts(name, cmark_node_get_literal(tmp));
    if (tmp != prev)
      cmark_node_free(tmp);
    tmp = next;
  }

  cmark_node_set_type(prev, CMARK_NODE_LINK);
  cmark_node_set_url(prev, cmark_strbuf_get(name));

  cmark_strbuf_free(name);

  return prev;
}
示例#3
0
static void md_autolink(cmark_iter *const iter) {
	regex_t linkify[1];
	// <http://daringfireball.net/2010/07/improved_regex_for_matching_urls>
	// Painstakingly ported to POSIX
	int rc = regcomp(linkify, "([a-z][a-z0-9_-]+:(/{1,3}|[a-z0-9%])|www[0-9]{0,3}[.]|[a-z0-9.-]+[.][a-z]{2,4}/)([^[:space:]()<>]+|\\(([^[:space:]()<>]+|(\\([^[:space:]()<>]+\\)))*\\))+(\\(([^[:space:]()<>]+|(\\([^[:space:]()<>]+\\)))*\\)|[^][[:space:]`!(){};:'\".,<>?«»“”‘’])", REG_ICASE | REG_EXTENDED);
	assert(0 == rc);

	for(;;) {
		cmark_event_type const event = cmark_iter_next(iter);
		if(CMARK_EVENT_DONE == event) break;
		if(CMARK_EVENT_ENTER != event) continue;
		cmark_node *const node = cmark_iter_get_node(iter);
		if(CMARK_NODE_TEXT != cmark_node_get_type(node)) continue;

		char const *const str = cmark_node_get_literal(node);
		char const *pos = str;
		regmatch_t match;
		while(0 == regexec(linkify, pos, 1, &match, 0)) {
			regoff_t const loc = match.rm_so;
			regoff_t const len = match.rm_eo - match.rm_so;

			char *pfx = strndup(pos, loc);
			char *link_abs = strndup(pos+loc, len);
			char *link_rel = aasprintf("/history/%s", link_abs);
			assert(pfx);
			assert(link_abs);
			assert(link_rel);

			cmark_node *text = cmark_node_new(CMARK_NODE_TEXT);
			cmark_node_set_literal(text, pfx);
			cmark_node *link = cmark_node_new(CMARK_NODE_LINK);
			cmark_node_set_url(link, link_rel);
			cmark_node *sup = superscript("^", "", link_abs);
			cmark_node *face = cmark_node_new(CMARK_NODE_TEXT);
			cmark_node_set_literal(face, link_abs);
			cmark_node_append_child(link, face);
			cmark_node_insert_before(node, text);
			cmark_node_insert_before(node, link);
			cmark_node_insert_before(node, sup);

			free(pfx); pfx = NULL;
			free(link_abs); link_abs = NULL;
			free(link_rel); link_rel = NULL;

			pos += loc+len;
		}

		if(str != pos) {
			cmark_node *text = cmark_node_new(CMARK_NODE_TEXT);
			cmark_node_set_literal(text, pos);
			cmark_node_insert_before(node, text);
			cmark_node_free(node);
		}

	}
	regfree(linkify);
}
示例#4
0
static void md_escape_inline(cmark_iter *const iter) {
	for(;;) {
		cmark_event_type const event = cmark_iter_next(iter);
		if(CMARK_EVENT_DONE == event) break;
		if(CMARK_EVENT_ENTER != event) continue;
		cmark_node *const node = cmark_iter_get_node(iter);
		if(CMARK_NODE_INLINE_HTML != cmark_node_get_type(node)) continue;

		char const *const str = cmark_node_get_literal(node);
		cmark_node *text = cmark_node_new(CMARK_NODE_TEXT);
		cmark_node_set_literal(text, str);
		cmark_node_insert_before(node, text);
		cmark_node_free(node);
	}
}
示例#5
0
文件: commonmark.c 项目: PKRoma/cmark
static int S_render_node(cmark_renderer *renderer, cmark_node *node,
                         cmark_event_type ev_type, int options) {
  cmark_node *tmp;
  int list_number;
  cmark_delim_type list_delim;
  int numticks;
  bool extra_spaces;
  int i;
  bool entering = (ev_type == CMARK_EVENT_ENTER);
  const char *info, *code, *title;
  char fencechar[2] = {'\0', '\0'};
  size_t info_len, code_len;
  char listmarker[LISTMARKER_SIZE];
  char *emph_delim;
  bool first_in_list_item;
  bufsize_t marker_width;
  bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options) &&
                    !(CMARK_OPT_HARDBREAKS & options);

  // Don't adjust tight list status til we've started the list.
  // Otherwise we loose the blank line between a paragraph and
  // a following list.
  if (!(node->type == CMARK_NODE_ITEM && node->prev == NULL && entering)) {
    tmp = get_containing_block(node);
    renderer->in_tight_list_item =
        tmp && // tmp might be NULL if there is no containing block
        ((tmp->type == CMARK_NODE_ITEM &&
          cmark_node_get_list_tight(tmp->parent)) ||
         (tmp && tmp->parent && tmp->parent->type == CMARK_NODE_ITEM &&
          cmark_node_get_list_tight(tmp->parent->parent)));
  }

  switch (node->type) {
  case CMARK_NODE_DOCUMENT:
    break;

  case CMARK_NODE_BLOCK_QUOTE:
    if (entering) {
      LIT("> ");
      renderer->begin_content = true;
      cmark_strbuf_puts(renderer->prefix, "> ");
    } else {
      cmark_strbuf_truncate(renderer->prefix, renderer->prefix->size - 2);
      BLANKLINE();
    }
    break;

  case CMARK_NODE_LIST:
    if (!entering && node->next && (node->next->type == CMARK_NODE_CODE_BLOCK ||
                                    node->next->type == CMARK_NODE_LIST)) {
      // this ensures that a following indented code block or list will be
      // inteprereted correctly.
      CR();
      LIT("<!-- end list -->");
      BLANKLINE();
    }
    break;

  case CMARK_NODE_ITEM:
    if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
      marker_width = 4;
    } else {
      list_number = cmark_node_get_list_start(node->parent);
      list_delim = cmark_node_get_list_delim(node->parent);
      tmp = node;
      while (tmp->prev) {
        tmp = tmp->prev;
        list_number += 1;
      }
      // we ensure a width of at least 4 so
      // we get nice transition from single digits
      // to double
      snprintf(listmarker, LISTMARKER_SIZE, "%d%s%s", list_number,
               list_delim == CMARK_PAREN_DELIM ? ")" : ".",
               list_number < 10 ? "  " : " ");
      marker_width = strlen(listmarker);
    }
    if (entering) {
      if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
        LIT("  - ");
        renderer->begin_content = true;
      } else {
        LIT(listmarker);
        renderer->begin_content = true;
      }
      for (i = marker_width; i--;) {
        cmark_strbuf_putc(renderer->prefix, ' ');
      }
    } else {
      cmark_strbuf_truncate(renderer->prefix,
                            renderer->prefix->size - marker_width);
      CR();
    }
    break;

  case CMARK_NODE_HEADING:
    if (entering) {
      for (i = cmark_node_get_heading_level(node); i > 0; i--) {
        LIT("#");
      }
      LIT(" ");
      renderer->begin_content = true;
      renderer->no_linebreaks = true;
    } else {
      renderer->no_linebreaks = false;
      BLANKLINE();
    }
    break;

  case CMARK_NODE_CODE_BLOCK:
    first_in_list_item = node->prev == NULL && node->parent &&
                         node->parent->type == CMARK_NODE_ITEM;

    if (!first_in_list_item) {
      BLANKLINE();
    }
    info = cmark_node_get_fence_info(node);
    info_len = strlen(info);
    fencechar[0] = strchr(info, '`') == NULL ? '`' : '~';
    code = cmark_node_get_literal(node);
    code_len = strlen(code);
    // use indented form if no info, and code doesn't
    // begin or end with a blank line, and code isn't
    // first thing in a list item
    if (info_len == 0 && (code_len > 2 && !cmark_isspace(code[0]) &&
                          !(cmark_isspace(code[code_len - 1]) &&
                            cmark_isspace(code[code_len - 2]))) &&
        !first_in_list_item) {
      LIT("    ");
      cmark_strbuf_puts(renderer->prefix, "    ");
      OUT(cmark_node_get_literal(node), false, LITERAL);
      cmark_strbuf_truncate(renderer->prefix, renderer->prefix->size - 4);
    } else {
      numticks = longest_backtick_sequence(code) + 1;
      if (numticks < 3) {
        numticks = 3;
      }
      for (i = 0; i < numticks; i++) {
        LIT(fencechar);
      }
      LIT(" ");
      OUT(info, false, LITERAL);
      CR();
      OUT(cmark_node_get_literal(node), false, LITERAL);
      CR();
      for (i = 0; i < numticks; i++) {
        LIT(fencechar);
      }
    }
    BLANKLINE();
    break;

  case CMARK_NODE_HTML_BLOCK:
    BLANKLINE();
    OUT(cmark_node_get_literal(node), false, LITERAL);
    BLANKLINE();
    break;

  case CMARK_NODE_CUSTOM_BLOCK:
    BLANKLINE();
    OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node),
        false, LITERAL);
    BLANKLINE();
    break;

  case CMARK_NODE_THEMATIC_BREAK:
    BLANKLINE();
    LIT("-----");
    BLANKLINE();
    break;

  case CMARK_NODE_PARAGRAPH:
    if (!entering) {
      BLANKLINE();
    }
    break;

  case CMARK_NODE_TEXT:
    OUT(cmark_node_get_literal(node), allow_wrap, NORMAL);
    break;

  case CMARK_NODE_LINEBREAK:
    if (!(CMARK_OPT_HARDBREAKS & options)) {
      LIT("  ");
    }
    CR();
    break;

  case CMARK_NODE_SOFTBREAK:
    if (CMARK_OPT_HARDBREAKS & options) {
      LIT("  ");
      CR();
    } else if (!renderer->no_linebreaks && renderer->width == 0 &&
               !(CMARK_OPT_HARDBREAKS & options) &&
               !(CMARK_OPT_NOBREAKS & options)) {
      CR();
    } else {
      OUT(" ", allow_wrap, LITERAL);
    }
    break;

  case CMARK_NODE_CODE:
    code = cmark_node_get_literal(node);
    code_len = strlen(code);
    numticks = shortest_unused_backtick_sequence(code);
    extra_spaces = code_len == 0 ||
	    code[0] == '`' || code[code_len - 1] == '`' ||
	    code[0] == ' ' || code[code_len - 1] == ' ';
    for (i = 0; i < numticks; i++) {
      LIT("`");
    }
    if (extra_spaces) {
      LIT(" ");
    }
    OUT(cmark_node_get_literal(node), allow_wrap, LITERAL);
    if (extra_spaces) {
      LIT(" ");
    }
    for (i = 0; i < numticks; i++) {
      LIT("`");
    }
    break;

  case CMARK_NODE_HTML_INLINE:
    OUT(cmark_node_get_literal(node), false, LITERAL);
    break;

  case CMARK_NODE_CUSTOM_INLINE:
    OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node),
        false, LITERAL);
    break;

  case CMARK_NODE_STRONG:
    if (entering) {
      LIT("**");
    } else {
      LIT("**");
    }
    break;

  case CMARK_NODE_EMPH:
    // If we have EMPH(EMPH(x)), we need to use *_x_*
    // because **x** is STRONG(x):
    if (node->parent && node->parent->type == CMARK_NODE_EMPH &&
        node->next == NULL && node->prev == NULL) {
      emph_delim = "_";
    } else {
      emph_delim = "*";
    }
    if (entering) {
      LIT(emph_delim);
    } else {
      LIT(emph_delim);
    }
    break;

  case CMARK_NODE_LINK:
    if (is_autolink(node)) {
      if (entering) {
        LIT("<");
        if (strncmp(cmark_node_get_url(node), "mailto:", 7) == 0) {
          LIT((const char *)cmark_node_get_url(node) + 7);
        } else {
          LIT((const char *)cmark_node_get_url(node));
        }
        LIT(">");
        // return signal to skip contents of node...
        return 0;
      }
    } else {
      if (entering) {
        LIT("[");
      } else {
        LIT("](");
        OUT(cmark_node_get_url(node), false, URL);
        title = cmark_node_get_title(node);
        if (strlen(title) > 0) {
          LIT(" \"");
          OUT(title, false, TITLE);
          LIT("\"");
        }
        LIT(")");
      }
    }
    break;

  case CMARK_NODE_IMAGE:
    if (entering) {
      LIT("![");
    } else {
      LIT("](");
      OUT(cmark_node_get_url(node), false, URL);
      title = cmark_node_get_title(node);
      if (strlen(title) > 0) {
        OUT(" \"", allow_wrap, LITERAL);
        OUT(title, false, TITLE);
        LIT("\"");
      }
      LIT(")");
    }
    break;

  default:
    assert(false);
    break;
  }

  return 1;
}
示例#6
0
static int S_render_node(cmark_renderer *renderer, cmark_node *node,
                         cmark_event_type ev_type, int options) {
  int list_number;
  int enumlevel;
  char list_number_string[LIST_NUMBER_STRING_SIZE];
  bool entering = (ev_type == CMARK_EVENT_ENTER);
  cmark_list_type list_type;
  bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options);

  // avoid warning about unused parameter:
  (void)(options);

  switch (node->type) {
  case CMARK_NODE_DOCUMENT:
    break;

  case CMARK_NODE_BLOCK_QUOTE:
    if (entering) {
      LIT("\\begin{quote}");
      CR();
    } else {
      LIT("\\end{quote}");
      BLANKLINE();
    }
    break;

  case CMARK_NODE_LIST:
    list_type = cmark_node_get_list_type(node);
    if (entering) {
      LIT("\\begin{");
      LIT(list_type == CMARK_ORDERED_LIST ? "enumerate" : "itemize");
      LIT("}");
      CR();
      list_number = cmark_node_get_list_start(node);
      if (list_number > 1) {
	enumlevel = S_get_enumlevel(node);
	// latex normally supports only five levels
	if (enumlevel >= 1 && enumlevel <= 5) {
          snprintf(list_number_string, LIST_NUMBER_STRING_SIZE, "%d",
                   list_number);
          LIT("\\setcounter{enum");
          switch(enumlevel) {
	  case 1: LIT("i"); break;
	  case 2: LIT("ii"); break;
	  case 3: LIT("iii"); break;
	  case 4: LIT("iv"); break;
	  case 5: LIT("v"); break;
	  default: LIT("i"); break;
	  }
          LIT("}{");
          OUT(list_number_string, false, NORMAL);
          LIT("}");
	}
        CR();
      }
    } else {
      LIT("\\end{");
      LIT(list_type == CMARK_ORDERED_LIST ? "enumerate" : "itemize");
      LIT("}");
      BLANKLINE();
    }
    break;

  case CMARK_NODE_ITEM:
    if (entering) {
      LIT("\\item ");
    } else {
      CR();
    }
    break;

  case CMARK_NODE_HEADING:
    if (entering) {
      switch (cmark_node_get_heading_level(node)) {
      case 1:
        LIT("\\section");
        break;
      case 2:
        LIT("\\subsection");
        break;
      case 3:
        LIT("\\subsubsection");
        break;
      case 4:
        LIT("\\paragraph");
        break;
      case 5:
        LIT("\\subparagraph");
        break;
      }
      LIT("{");
    } else {
      LIT("}");
      BLANKLINE();
    }
    break;

  case CMARK_NODE_CODE_BLOCK:
    CR();
    LIT("\\begin{verbatim}");
    CR();
    OUT(cmark_node_get_literal(node), false, LITERAL);
    CR();
    LIT("\\end{verbatim}");
    BLANKLINE();
    break;

  case CMARK_NODE_HTML_BLOCK:
    break;

  case CMARK_NODE_CUSTOM_BLOCK:
    CR();
    OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node),
        false, LITERAL);
    CR();
    break;

  case CMARK_NODE_THEMATIC_BREAK:
    BLANKLINE();
    LIT("\\begin{center}\\rule{0.5\\linewidth}{\\linethickness}\\end{center}");
    BLANKLINE();
    break;

  case CMARK_NODE_PARAGRAPH:
    if (!entering) {
      BLANKLINE();
    }
    break;

  case CMARK_NODE_TEXT:
    OUT(cmark_node_get_literal(node), allow_wrap, NORMAL);
    break;

  case CMARK_NODE_LINEBREAK:
    LIT("\\\\");
    CR();
    break;

  case CMARK_NODE_SOFTBREAK:
    if (options & CMARK_OPT_HARDBREAKS) {
      LIT("\\\\");
      CR();
    } else if (renderer->width == 0 && !(CMARK_OPT_NOBREAKS & options)) {
      CR();
    } else {
      OUT(" ", allow_wrap, NORMAL);
    }
    break;

  case CMARK_NODE_CODE:
    LIT("\\texttt{");
    OUT(cmark_node_get_literal(node), false, NORMAL);
    LIT("}");
    break;

  case CMARK_NODE_HTML_INLINE:
    break;

  case CMARK_NODE_CUSTOM_INLINE:
    OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node),
        false, LITERAL);
    break;

  case CMARK_NODE_STRONG:
    if (entering) {
      LIT("\\textbf{");
    } else {
      LIT("}");
    }
    break;

  case CMARK_NODE_EMPH:
    if (entering) {
      LIT("\\emph{");
    } else {
      LIT("}");
    }
    break;

  case CMARK_NODE_LINK:
    if (entering) {
      const char *url = cmark_node_get_url(node);
      // requires \usepackage{hyperref}
      switch (get_link_type(node)) {
      case URL_AUTOLINK:
        LIT("\\url{");
        OUT(url, false, URL);
        LIT("}");
        return 0; // Don't process further nodes to avoid double-rendering artefacts
      case EMAIL_AUTOLINK:
        LIT("\\href{");
        OUT(url, false, URL);
        LIT("}\\nolinkurl{");
        break;
      case NORMAL_LINK:
        LIT("\\href{");
        OUT(url, false, URL);
        LIT("}{");
        break;
      case INTERNAL_LINK:
        LIT("\\protect\\hyperlink{");
        OUT(url + 1, false, URL);
        LIT("}{");
        break;
      case NO_LINK:
        LIT("{"); // error?
      }
    } else {
      LIT("}");
    }

    break;

  case CMARK_NODE_IMAGE:
    if (entering) {
      LIT("\\protect\\includegraphics{");
      // requires \include{graphicx}
      OUT(cmark_node_get_url(node), false, URL);
      LIT("}");
      return 0;
    }
    break;

  default:
    assert(false);
    break;
  }

  return 1;
}
示例#7
0
文件: latex.c 项目: omgitsads/Twitch
static int
S_render_node(cmark_renderer *renderer,
	      cmark_node *node,
	      cmark_event_type ev_type,
	      int options)
{
	int list_number;
	char list_number_string[20];
	bool entering = (ev_type == CMARK_EVENT_ENTER);
	cmark_list_type list_type;
	const char* roman_numerals[] = { "", "i", "ii", "iii", "iv", "v",
	                                 "vi", "vii", "viii", "ix", "x"
	                               };

	// avoid warning about unused parameter:
	(void)(options);

	switch (node->type) {
	case CMARK_NODE_DOCUMENT:
		break;

	case CMARK_NODE_BLOCK_QUOTE:
		if (entering) {
			LIT("\\begin{quote}");
			CR();
		} else {
			LIT("\\end{quote}");
			BLANKLINE();
		}
		break;

	case CMARK_NODE_LIST:
		list_type = cmark_node_get_list_type(node);
		if (entering) {
			LIT("\\begin{");
			LIT(list_type == CMARK_ORDERED_LIST ?
			    "enumerate" : "itemize");
			LIT("}");
			CR();
			list_number = cmark_node_get_list_start(node);
			if (list_number > 1) {
				sprintf(list_number_string,
				         "%d", list_number);
				LIT("\\setcounter{enum");
				LIT((char *)roman_numerals[S_get_enumlevel(node)]);
				LIT("}{");
				OUT(list_number_string, false, NORMAL);
				LIT("}");
				CR();
			}
		} else {
			LIT("\\end{");
			LIT(list_type == CMARK_ORDERED_LIST ?
			    "enumerate" : "itemize");
			LIT("}");
			BLANKLINE();
		}
		break;

	case CMARK_NODE_ITEM:
		if (entering) {
			LIT("\\item ");
		} else {
			CR();
		}
		break;

	case CMARK_NODE_HEADER:
		if (entering) {
			switch (cmark_node_get_header_level(node)) {
			case 1:
				LIT("\\section");
				break;
			case 2:
				LIT("\\subsection");
				break;
			case 3:
				LIT("\\subsubsection");
				break;
			case 4:
				LIT("\\paragraph");
				break;
			case 5:
				LIT("\\subparagraph");
				break;
			}
			LIT("{");
		} else {
			LIT("}");
			BLANKLINE();
		}
		break;

	case CMARK_NODE_CODE_BLOCK:
		CR();
		LIT("\\begin{verbatim}");
		CR();
		OUT(cmark_node_get_literal(node), false, LITERAL);
		CR();
		LIT("\\end{verbatim}");
		BLANKLINE();
		break;

	case CMARK_NODE_HTML:
		break;

	case CMARK_NODE_HRULE:
		BLANKLINE();
		LIT("\\begin{center}\\rule{0.5\\linewidth}{\\linethickness}\\end{center}");
		BLANKLINE();
		break;

	case CMARK_NODE_PARAGRAPH:
		if (!entering) {
			BLANKLINE();
		}
		break;

	case CMARK_NODE_TEXT:
		OUT(cmark_node_get_literal(node), true, NORMAL);
		break;

	case CMARK_NODE_LINEBREAK:
		LIT("\\\\");
		CR();
		break;

	case CMARK_NODE_SOFTBREAK:
		if (renderer->width == 0) {
			CR();
		} else {
			OUT(" ", true, NORMAL);
		}
		break;

	case CMARK_NODE_CODE:
		LIT("\\texttt{");
		OUT(cmark_node_get_literal(node), false, NORMAL);
		LIT("}");
		break;

	case CMARK_NODE_INLINE_HTML:
		break;

	case CMARK_NODE_STRONG:
		if (entering) {
			LIT("\\textbf{");
		} else {
			LIT("}");
		}
		break;

	case CMARK_NODE_EMPH:
		if (entering) {
			LIT("\\emph{");
		} else {
			LIT("}");
		}
		break;

	case CMARK_NODE_LINK:
		if (entering) {
			const char* url = cmark_node_get_url(node);
			// requires \usepackage{hyperref}
			switch(get_link_type(node)) {
			case URL_AUTOLINK:
				LIT("\\url{");
				OUT(url, false, URL);
				break;
			case EMAIL_AUTOLINK:
				LIT("\\href{");
				OUT(url, false, URL);
				LIT("}\\nolinkurl{");
				break;
			case NORMAL_LINK:
				LIT("\\href{");
				OUT(url, false, URL);
				LIT("}{");
				break;
			case NO_LINK:
				LIT("{");  // error?
			}
		} else {
			LIT("}");
		}

		break;

	case CMARK_NODE_IMAGE:
		if (entering) {
			LIT("\\protect\\includegraphics{");
			// requires \include{graphicx}
			OUT(cmark_node_get_url(node), false, URL);
			LIT("}");
			return 0;
		}
		break;

	default:
		assert(false);
		break;
	}

	return 1;
}
示例#8
0
// Convert a single node.
static char*
S_node_to_pod(cmark_node *node, CFCClass *klass, int header_level) {
    char *result = CFCUtil_strdup("");
    if (node == NULL) {
        return result;
    }

    int found_matching_code_block = false;
    cmark_iter *iter = cmark_iter_new(node);
    cmark_event_type ev_type;

    while (CMARK_EVENT_DONE != (ev_type = cmark_iter_next(iter))) {
        cmark_node *node = cmark_iter_get_node(iter);
        cmark_node_type type = cmark_node_get_type(node);

        switch (type) {
            case CMARK_NODE_DOCUMENT:
                break;

            case CMARK_NODE_PARAGRAPH:
                if (ev_type == CMARK_EVENT_EXIT) {
                    result = CFCUtil_cat(result, "\n\n", NULL);
                }
                break;

            case CMARK_NODE_BLOCK_QUOTE:
            case CMARK_NODE_LIST:
                if (ev_type == CMARK_EVENT_ENTER) {
                    result = CFCUtil_cat(result, "=over\n\n", NULL);
                }
                else {
                    result = CFCUtil_cat(result, "=back\n\n", NULL);
                }
                break;

            case CMARK_NODE_ITEM:
                // TODO: Ordered lists.
                if (ev_type == CMARK_EVENT_ENTER) {
                    result = CFCUtil_cat(result, "=item *\n\n", NULL);
                }
                break;

            case CMARK_NODE_HEADER:
                if (ev_type == CMARK_EVENT_ENTER) {
                    int extra_level = cmark_node_get_header_level(node) - 1;
                    char *header = CFCUtil_sprintf("=head%d ",
                                                   header_level + extra_level);
                    result = CFCUtil_cat(result, header, NULL);
                    FREEMEM(header);
                }
                else {
                    result = CFCUtil_cat(result, "\n\n", NULL);
                }
                break;

            case CMARK_NODE_CODE_BLOCK: {
                int is_host = CFCMarkdown_code_block_is_host(node, "perl");

                if (is_host) {
                    found_matching_code_block = true;

                    const char *content = cmark_node_get_literal(node);
                    char *copy = CFCUtil_strdup(content);
                    // Chomp trailing newline.
                    size_t len = strlen(copy);
                    if (len > 0 && copy[len-1] == '\n') {
                        copy[len-1] = '\0';
                    }
                    char *indented
                        = CFCUtil_global_replace(copy, "\n", "\n    ");
                    result
                        = CFCUtil_cat(result, "    ", indented, "\n\n", NULL);
                    FREEMEM(indented);
                    FREEMEM(copy);
                }

                if (CFCMarkdown_code_block_is_last(node)) {
                    if (!found_matching_code_block) {
                        result = CFCUtil_cat(result,
                            "    Code example for Perl is missing\n\n");
                    }
                    else {
                        // Reset.
                        found_matching_code_block = false;
                    }
                }

                break;
            }

            case CMARK_NODE_HTML: {
                const char *html = cmark_node_get_literal(node);
                result = CFCUtil_cat(result, "=begin html\n\n", html,
                                     "\n=end\n\n", NULL);
                break;
            }

            case CMARK_NODE_HRULE:
                break;

            case CMARK_NODE_TEXT: {
                const char *content = cmark_node_get_literal(node);
                char *escaped = S_pod_escape(content);
                result = CFCUtil_cat(result, escaped, NULL);
                FREEMEM(escaped);
                break;
            }

            case CMARK_NODE_LINEBREAK:
                // POD doesn't support line breaks. Start a new paragraph.
                result = CFCUtil_cat(result, "\n\n", NULL);
                break;

            case CMARK_NODE_SOFTBREAK:
                result = CFCUtil_cat(result, "\n", NULL);
                break;

            case CMARK_NODE_CODE: {
                const char *content = cmark_node_get_literal(node);
                char *escaped = S_pod_escape(content);
                result = CFCUtil_cat(result, "C<", escaped, ">", NULL);
                FREEMEM(escaped);
                break;
            }

            case CMARK_NODE_INLINE_HTML: {
                const char *html = cmark_node_get_literal(node);
                CFCUtil_warn("Inline HTML not supported in POD: %s", html);
                break;
            }

            case CMARK_NODE_LINK:
                if (ev_type == CMARK_EVENT_ENTER) {
                    char *pod = S_convert_link(node, klass, header_level);
                    result = CFCUtil_cat(result, pod, NULL);
                    FREEMEM(pod);
                    cmark_iter_reset(iter, node, CMARK_EVENT_EXIT);
                }
                break;

            case CMARK_NODE_IMAGE:
                CFCUtil_warn("Images not supported in POD");
                break;

            case CMARK_NODE_STRONG:
                if (ev_type == CMARK_EVENT_ENTER) {
                    result = CFCUtil_cat(result, "B<", NULL);
                }
                else {
                    result = CFCUtil_cat(result, ">", NULL);
                }
                break;

            case CMARK_NODE_EMPH:
                if (ev_type == CMARK_EVENT_ENTER) {
                    result = CFCUtil_cat(result, "I<", NULL);
                }
                else {
                    result = CFCUtil_cat(result, ">", NULL);
                }
                break;

            default:
                CFCUtil_die("Invalid cmark node type: %d", (int)type);
                break;
        }
    }

    cmark_iter_free(iter);
    return result;
}
示例#9
0
static cmark_node *fixup_nodes(cmark_syntax_extension *self,
                               cmark_parser *parser,
                               cmark_inline_parser *inline_parser,
                               cmark_node *parent,
                               int start_offset,
                               int size)
{
    int node_text_len;
    cmark_node *prev = NULL;
    cmark_node *tmp;
    int name_size = size;
    cmark_strbuf *name;
    NamedLink *named_link;

    for (prev = cmark_node_last_child(parent); prev; prev = cmark_node_previous(prev)) {
        if (cmark_node_get_type(prev) == CMARK_NODE_TEXT) {
            const char *text = cmark_node_get_literal(prev);
            node_text_len = strlen(text);
            size -= node_text_len;

            if (size <= 0) {
                if (size < 0) {
                    char *split_text = my_strndup(text, size * -1);
                    cmark_node *split = cmark_node_new(CMARK_NODE_TEXT);

                    cmark_node_set_literal(split, split_text);
                    free(split_text);

                    split_text = my_strndup(text + (size * - 1), node_text_len - size);
                    cmark_node_set_literal(prev, split_text);
                    free(split_text);

                    cmark_node_insert_before(prev, split);
                }
                break;
            }
        } else {
            return NULL;
        }
    }

    name = cmark_strbuf_new(name_size + 1);

    tmp = prev;

    while (tmp) {
        cmark_node *next = cmark_node_next(tmp);

        cmark_strbuf_puts(name, cmark_node_get_literal(tmp));
        if (tmp != prev)
            cmark_node_free(tmp);
        tmp = next;
    }

    named_link = PRIV(self)->link_resolve_func(cmark_strbuf_get(name));

    if (!named_link || !named_link->ref) {
        int actual_line, actual_col;

        translate_sourcepos(get_first_parent_block(parent),
                            start_offset, &actual_line, &actual_col);

        cmark_strbuf *message = cmark_strbuf_new(0);
        cmark_strbuf_puts(message, "Trying to link to non-existing symbol ‘");
        cmark_strbuf_puts(message, cmark_strbuf_get(name));
        cmark_strbuf_puts(message, "’");
        diagnose("gtk-doc-bad-link", cmark_strbuf_get(message), actual_line - 1,
                 actual_col - 1);
        cmark_strbuf_free(message);
        cmark_node_set_literal(prev, cmark_strbuf_get(name));
        cmark_strbuf_free(name);
        return prev;
    }

    free_named_link(named_link);

    cmark_node_set_type(prev, CMARK_NODE_LINK);
    cmark_node_set_url(prev, cmark_strbuf_get(name));

    cmark_strbuf_free(name);

    return prev;
}
示例#10
0
static int
S_render_node(cmark_renderer *renderer,
              cmark_node *node,
              cmark_event_type ev_type,
              int options)
{
	cmark_node *tmp;
	int list_number;
	cmark_delim_type list_delim;
	int numticks;
	int i;
	bool entering = (ev_type == CMARK_EVENT_ENTER);
	const char *info, *code, *title;
	size_t info_len, code_len;
	cmark_strbuf listmarker = GH_BUF_INIT;
	char *emph_delim;
	bufsize_t marker_width;

	// Don't adjust tight list status til we've started the list.
	// Otherwise we loose the blank line between a paragraph and
	// a following list.
	if (!(node->type == CMARK_NODE_ITEM && node->prev == NULL &&
	      entering)) {
		tmp = get_containing_block(node);
		renderer->in_tight_list_item =
		    (tmp->type == CMARK_NODE_ITEM &&
		     cmark_node_get_list_tight(tmp->parent)) ||
		    (tmp &&
		     tmp->parent &&
		     tmp->parent->type == CMARK_NODE_ITEM &&
		     cmark_node_get_list_tight(tmp->parent->parent));
	}

	switch (node->type) {
	case CMARK_NODE_DOCUMENT:
		break;

	case CMARK_NODE_BLOCK_QUOTE:
		if (entering) {
			LIT("> ");
			cmark_strbuf_puts(renderer->prefix, "> ");
		} else {
			cmark_strbuf_truncate(renderer->prefix,
			                      renderer->prefix->size - 2);
			BLANKLINE();
		}
		break;

	case CMARK_NODE_LIST:
		if (!entering && node->next &&
		    (node->next->type == CMARK_NODE_CODE_BLOCK ||
		     node->next->type == CMARK_NODE_LIST)) {
			// this ensures 2 blank lines after list,
			// if before code block or list:
			LIT("\n");
		}
		break;

	case CMARK_NODE_ITEM:
		if (cmark_node_get_list_type(node->parent) ==
		    CMARK_BULLET_LIST) {
			marker_width = 2;
		} else {
			list_number = cmark_node_get_list_start(node->parent);
			list_delim = cmark_node_get_list_delim(node->parent);
			tmp = node;
			while (tmp->prev) {
				tmp = tmp->prev;
				list_number += 1;
			}
			// we ensure a width of at least 4 so
			// we get nice transition from single digits
			// to double
			cmark_strbuf_printf(&listmarker,
			                    "%d%s%s", list_number,
			                    list_delim == CMARK_PAREN_DELIM ?
			                    ")" : ".",
			                    list_number < 10 ? "  " : " ");
			marker_width = listmarker.size;
		}
		if (entering) {
			if (cmark_node_get_list_type(node->parent) ==
			    CMARK_BULLET_LIST) {
				LIT("* ");
				cmark_strbuf_puts(renderer->prefix, "  ");
			} else {
				LIT((char *)listmarker.ptr);
				for (i = marker_width; i--;) {
					cmark_strbuf_putc(renderer->prefix, ' ');
				}
			}
		} else {
			cmark_strbuf_truncate(renderer->prefix,
			                      renderer->prefix->size -
			                      marker_width);
			CR();
		}
		cmark_strbuf_free(&listmarker);
		break;

	case CMARK_NODE_HEADER:
		if (entering) {
			for (int i = cmark_node_get_header_level(node); i > 0; i--) {
				LIT("#");
			}
			LIT(" ");
			renderer->no_wrap = true;
		} else {
			renderer->no_wrap = false;
			BLANKLINE();
		}
		break;

	case CMARK_NODE_CODE_BLOCK:
		BLANKLINE();
		info = cmark_node_get_fence_info(node);
		info_len = safe_strlen(info);
		code = cmark_node_get_literal(node);
		code_len = safe_strlen(code);
		// use indented form if no info, and code doesn't
		// begin or end with a blank line, and code isn't
		// first thing in a list item
		if (info_len == 0 &&
		    (code_len > 2 &&
		     !isspace(code[0]) &&
		     !(isspace(code[code_len - 1]) &&
		       isspace(code[code_len - 2]))) &&
		    !(node->prev == NULL && node->parent &&
		      node->parent->type == CMARK_NODE_ITEM)) {
			LIT("    ");
			cmark_strbuf_puts(renderer->prefix, "    ");
			OUT(cmark_node_get_literal(node), false, LITERAL);
			cmark_strbuf_truncate(renderer->prefix,
			                      renderer->prefix->size - 4);
		} else {
			numticks = longest_backtick_sequence(code) + 1;
			if (numticks < 3) {
				numticks = 3;
			}
			for (i = 0; i < numticks; i++) {
				LIT("`");
			}
			LIT(" ");
			OUT(info, false, LITERAL);
			CR();
			OUT(cmark_node_get_literal(node), false, LITERAL);
			CR();
			for (i = 0; i < numticks; i++) {
				LIT("`");
			}
		}
		BLANKLINE();
		break;

	case CMARK_NODE_HTML:
		BLANKLINE();
		OUT(cmark_node_get_literal(node), false, LITERAL);
		BLANKLINE();
		break;

	case CMARK_NODE_HRULE:
		BLANKLINE();
		LIT("-----");
		BLANKLINE();
		break;

	case CMARK_NODE_PARAGRAPH:
		if (!entering) {
			BLANKLINE();
		}
		break;

	case CMARK_NODE_TEXT:
		OUT(cmark_node_get_literal(node), true, NORMAL);
		break;

	case CMARK_NODE_LINEBREAK:
		if (!(CMARK_OPT_HARDBREAKS & options)) {
			LIT("\\");
		}
		CR();
		break;

	case CMARK_NODE_SOFTBREAK:
		if (renderer->width == 0 &&
		    !(CMARK_OPT_HARDBREAKS & options)) {
			CR();
		} else {
			OUT(" ", true, LITERAL);
		}
		break;

	case CMARK_NODE_CODE:
		code = cmark_node_get_literal(node);
		code_len = safe_strlen(code);
		numticks = shortest_unused_backtick_sequence(code);
		for (i = 0; i < numticks; i++) {
			LIT("`");
		}
		if (code_len == 0 || code[0] == '`') {
			LIT(" ");
		}
		OUT(cmark_node_get_literal(node), true, LITERAL);
		if (code_len == 0 || code[code_len - 1] == '`') {
			LIT(" ");
		}
		for (i = 0; i < numticks; i++) {
			LIT("`");
		}
		break;

	case CMARK_NODE_INLINE_HTML:
		OUT(cmark_node_get_literal(node), false, LITERAL);
		break;

	case CMARK_NODE_STRONG:
		if (entering) {
			LIT("**");
		} else {
			LIT("**");
		}
		break;

	case CMARK_NODE_EMPH:
		// If we have EMPH(EMPH(x)), we need to use *_x_*
		// because **x** is STRONG(x):
		if (node->parent && node->parent->type == CMARK_NODE_EMPH &&
		    node->next == NULL && node->prev == NULL) {
			emph_delim = "_";
		} else {
			emph_delim = "*";
		}
		if (entering) {
			LIT(emph_delim);
		} else {
			LIT(emph_delim);
		}
		break;

	case CMARK_NODE_LINK:
		if (is_autolink(node)) {
			if (entering) {
				LIT("<");
				if (strncmp(cmark_node_get_url(node),
				            "mailto:", 7) == 0) {
					LIT((char *)cmark_node_get_url(node) + 7);
				} else {
					LIT((char *)cmark_node_get_url(node));
				}
				LIT(">");
				// return signal to skip contents of node...
				return 0;
			}
		} else {
			if (entering) {
				LIT("[");
			} else {
				LIT("](");
				OUT(cmark_node_get_url(node), false, URL);
				title = cmark_node_get_title(node);
				if (safe_strlen(title) > 0) {
					LIT(" \"");
					OUT(title, false, TITLE);
					LIT("\"");
				}
				LIT(")");
			}
		}
		break;

	case CMARK_NODE_IMAGE:
		if (entering) {
			LIT("![");
		} else {
			LIT("](");
			OUT(cmark_node_get_url(node), false, URL);
			title = cmark_node_get_title(node);
			if (safe_strlen(title) > 0) {
				OUT(" \"", true, LITERAL);
				OUT(title, false, TITLE);
				LIT("\"");
			}
			LIT(")");
		}
		break;

	default:
		assert(false);
		break;
	}

	return 1;
}
示例#11
0
文件: man.c 项目: mbenelli/cmark
static int S_render_node(cmark_renderer *renderer, cmark_node *node,
                         cmark_event_type ev_type, int options) {
  cmark_node *tmp;
  int list_number;
  bool entering = (ev_type == CMARK_EVENT_ENTER);

  // avoid unused parameter error:
  (void)(options);

  switch (node->type) {
  case CMARK_NODE_DOCUMENT:
    break;

  case CMARK_NODE_BLOCK_QUOTE:
    if (entering) {
      CR();
      LIT(".RS");
      CR();
    } else {
      CR();
      LIT(".RE");
      CR();
    }
    break;

  case CMARK_NODE_LIST:
    break;

  case CMARK_NODE_ITEM:
    if (entering) {
      CR();
      LIT(".IP ");
      if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
        LIT("\\[bu] 2");
      } else {
        list_number = cmark_node_get_list_start(node->parent);
        tmp = node;
        while (tmp->prev) {
          tmp = tmp->prev;
          list_number += 1;
        }
        const size_t LIST_NUMBER_SIZE = 20;
        char list_number_s[LIST_NUMBER_SIZE];
        snprintf(list_number_s, LIST_NUMBER_SIZE, "\"%d.\" 4", list_number);
        LIT(list_number_s);
      }
      CR();
    } else {
      CR();
    }
    break;

  case CMARK_NODE_HEADER:
    if (entering) {
      CR();
      LIT(cmark_node_get_header_level(node) == 1 ? ".SH" : ".SS");
      CR();
    } else {
      CR();
    }
    break;

  case CMARK_NODE_CODE_BLOCK:
    CR();
    LIT(".IP\n.nf\n\\f[C]\n");
    OUT(cmark_node_get_literal(node), false, NORMAL);
    CR();
    LIT("\\f[]\n.fi");
    CR();
    break;

  case CMARK_NODE_HTML:
    break;

  case CMARK_NODE_HRULE:
    CR();
    LIT(".PP\n  *  *  *  *  *");
    CR();
    break;

  case CMARK_NODE_PARAGRAPH:
    if (entering) {
      // no blank line if first paragraph in list:
      if (node->parent && node->parent->type == CMARK_NODE_ITEM &&
          node->prev == NULL) {
        // no blank line or .PP
      } else {
        CR();
        LIT(".PP");
        CR();
      }
    } else {
      CR();
    }
    break;

  case CMARK_NODE_TEXT:
    OUT(cmark_node_get_literal(node), true, NORMAL);
    break;

  case CMARK_NODE_LINEBREAK:
    LIT(".PD 0\n.P\n.PD");
    CR();
    break;

  case CMARK_NODE_SOFTBREAK:
    if (renderer->width == 0) {
      CR();
    } else {
      OUT(" ", true, LITERAL);
    }
    break;

  case CMARK_NODE_CODE:
    LIT("\\f[C]");
    OUT(cmark_node_get_literal(node), true, NORMAL);
    LIT("\\f[]");
    break;

  case CMARK_NODE_INLINE_HTML:
    break;

  case CMARK_NODE_STRONG:
    if (entering) {
      LIT("\\f[B]");
    } else {
      LIT("\\f[]");
    }
    break;

  case CMARK_NODE_EMPH:
    if (entering) {
      LIT("\\f[I]");
    } else {
      LIT("\\f[]");
    }
    break;

  case CMARK_NODE_LINK:
    if (!entering) {
      LIT(" (");
      OUT(cmark_node_get_url(node), true, URL);
      LIT(")");
    }
    break;

  case CMARK_NODE_IMAGE:
    if (entering) {
      LIT("[IMAGE: ");
    } else {
      LIT("]");
    }
    break;

  default:
    assert(false);
    break;
  }

  return 1;
}
示例#12
0
文件: main.c 项目: txdv/cmark
static void
accessors(test_batch_runner *runner)
{
	static const char markdown[] =
		"## Header\n"
		"\n"
		"* Item 1\n"
		"* Item 2\n"
		"\n"
		"2. Item 1\n"
		"\n"
		"3. Item 2\n"
		"\n"
		"\n"
		"    code\n"
		"\n"
		"``` lang\n"
		"fenced\n"
		"```\n"
		"\n"
		"<div>html</div>\n"
		"\n"
		"[link](url 'title')\n";

	cmark_node *doc = cmark_parse_document(markdown, sizeof(markdown) - 1, CMARK_OPT_DEFAULT);

	// Getters

	cmark_node *header = cmark_node_first_child(doc);
	INT_EQ(runner, cmark_node_get_header_level(header), 2,
	       "get_header_level");

	cmark_node *bullet_list = cmark_node_next(header);
	INT_EQ(runner, cmark_node_get_list_type(bullet_list),
	       CMARK_BULLET_LIST, "get_list_type bullet");
	INT_EQ(runner, cmark_node_get_list_tight(bullet_list), 1,
	       "get_list_tight tight");

	cmark_node *ordered_list = cmark_node_next(bullet_list);
	INT_EQ(runner, cmark_node_get_list_type(ordered_list),
	       CMARK_ORDERED_LIST, "get_list_type ordered");
	INT_EQ(runner, cmark_node_get_list_delim(ordered_list),
	       CMARK_PERIOD_DELIM, "get_list_delim ordered");
	INT_EQ(runner, cmark_node_get_list_start(ordered_list), 2,
	       "get_list_start");
	INT_EQ(runner, cmark_node_get_list_tight(ordered_list), 0,
	       "get_list_tight loose");

	cmark_node *code = cmark_node_next(ordered_list);
	STR_EQ(runner, cmark_node_get_literal(code), "code\n",
	       "get_literal indented code");

	cmark_node *fenced = cmark_node_next(code);
	STR_EQ(runner, cmark_node_get_literal(fenced), "fenced\n",
	       "get_literal fenced code");
	STR_EQ(runner, cmark_node_get_fence_info(fenced), "lang",
	       "get_fence_info");

	cmark_node *html = cmark_node_next(fenced);
	STR_EQ(runner, cmark_node_get_literal(html),
	       "<div>html</div>\n", "get_literal html");

	cmark_node *paragraph = cmark_node_next(html);
	INT_EQ(runner, cmark_node_get_start_line(paragraph), 19,
	       "get_start_line");
	INT_EQ(runner, cmark_node_get_start_column(paragraph), 1,
	       "get_start_column");
	INT_EQ(runner, cmark_node_get_end_line(paragraph), 19,
	       "get_end_line");

	cmark_node *link = cmark_node_first_child(paragraph);
	STR_EQ(runner, cmark_node_get_url(link), "url",
	       "get_url");
	STR_EQ(runner, cmark_node_get_title(link), "title",
	       "get_title");

	cmark_node *string = cmark_node_first_child(link);
	STR_EQ(runner, cmark_node_get_literal(string), "link",
	       "get_literal string");

	// Setters

	OK(runner, cmark_node_set_header_level(header, 3),
	   "set_header_level");

	OK(runner, cmark_node_set_list_type(bullet_list, CMARK_ORDERED_LIST),
	   "set_list_type ordered");
	OK(runner, cmark_node_set_list_delim(bullet_list, CMARK_PAREN_DELIM),
	   "set_list_delim paren");
	OK(runner, cmark_node_set_list_start(bullet_list, 3),
	   "set_list_start");
	OK(runner, cmark_node_set_list_tight(bullet_list, 0),
	   "set_list_tight loose");

	OK(runner, cmark_node_set_list_type(ordered_list, CMARK_BULLET_LIST),
	   "set_list_type bullet");
	OK(runner, cmark_node_set_list_tight(ordered_list, 1),
	   "set_list_tight tight");

	OK(runner, cmark_node_set_literal(code, "CODE\n"),
	   "set_literal indented code");

	OK(runner, cmark_node_set_literal(fenced, "FENCED\n"),
	   "set_literal fenced code");
	OK(runner, cmark_node_set_fence_info(fenced, "LANG"),
	   "set_fence_info");

	OK(runner, cmark_node_set_literal(html, "<div>HTML</div>\n"),
	   "set_literal html");

	OK(runner, cmark_node_set_url(link, "URL"),
	   "set_url");
	OK(runner, cmark_node_set_title(link, "TITLE"),
	   "set_title");

	OK(runner, cmark_node_set_literal(string, "LINK"),
	   "set_literal string");

	char *rendered_html = cmark_render_html(doc, CMARK_OPT_DEFAULT);
	static const char expected_html[] =
		"<h3>Header</h3>\n"
		"<ol start=\"3\">\n"
		"<li>\n"
		"<p>Item 1</p>\n"
		"</li>\n"
		"<li>\n"
		"<p>Item 2</p>\n"
		"</li>\n"
		"</ol>\n"
		"<ul>\n"
		"<li>Item 1</li>\n"
		"<li>Item 2</li>\n"
		"</ul>\n"
		"<pre><code>CODE\n"
		"</code></pre>\n"
		"<pre><code class=\"language-LANG\">FENCED\n"
		"</code></pre>\n"
		"<div>HTML</div>\n"
		"<p><a href=\"URL\" title=\"TITLE\">LINK</a></p>\n";
	STR_EQ(runner, rendered_html, expected_html, "setters work");
	free(rendered_html);

	// Getter errors

	INT_EQ(runner, cmark_node_get_header_level(bullet_list), 0,
	       "get_header_level error");
	INT_EQ(runner, cmark_node_get_list_type(header), CMARK_NO_LIST,
	       "get_list_type error");
	INT_EQ(runner, cmark_node_get_list_start(code), 0,
	       "get_list_start error");
	INT_EQ(runner, cmark_node_get_list_tight(fenced), 0,
	       "get_list_tight error");
	OK(runner, cmark_node_get_literal(ordered_list) == NULL,
	   "get_literal error");
	OK(runner, cmark_node_get_fence_info(paragraph) == NULL,
	   "get_fence_info error");
	OK(runner, cmark_node_get_url(html) == NULL,
	   "get_url error");
	OK(runner, cmark_node_get_title(header) == NULL,
	   "get_title error");

	// Setter errors

	OK(runner, !cmark_node_set_header_level(bullet_list, 3),
	   "set_header_level error");
	OK(runner, !cmark_node_set_list_type(header, CMARK_ORDERED_LIST),
	   "set_list_type error");
	OK(runner, !cmark_node_set_list_start(code, 3),
	   "set_list_start error");
	OK(runner, !cmark_node_set_list_tight(fenced, 0),
	   "set_list_tight error");
	OK(runner, !cmark_node_set_literal(ordered_list, "content\n"),
	   "set_literal error");
	OK(runner, !cmark_node_set_fence_info(paragraph, "lang"),
	   "set_fence_info error");
	OK(runner, !cmark_node_set_url(html, "url"),
	   "set_url error");
	OK(runner, !cmark_node_set_title(header, "title"),
	   "set_title error");

	OK(runner, !cmark_node_set_header_level(header, 0),
	   "set_header_level too small");
	OK(runner, !cmark_node_set_header_level(header, 7),
	   "set_header_level too large");
	OK(runner, !cmark_node_set_list_type(bullet_list, CMARK_NO_LIST),
	   "set_list_type invalid");
	OK(runner, !cmark_node_set_list_start(bullet_list, -1),
	   "set_list_start negative");

	cmark_node_free(doc);
}
示例#13
0
static int S_render_node(cmark_renderer *renderer, cmark_node *node,
                         cmark_event_type ev_type, int options) {
  cmark_node *tmp;
  int list_number;
  bool entering = (ev_type == CMARK_EVENT_ENTER);
  bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options);

  if (node->extension && node->extension->man_render_func) {
    node->extension->man_render_func(node->extension, renderer, node, ev_type, options);
    return 1;
  }

  switch (node->type) {
  case CMARK_NODE_DOCUMENT:
    if (entering) {
      /* Define a strikethrough macro */
      /* Commenting out because this makes tests fail
      LIT(".de ST");
      CR();
      LIT(".nr ww \\w'\\\\$1'");
      CR();
      LIT("\\Z@\\v'-.25m'\\l'\\\\n[ww]u'@\\\\$1");
      CR();
      LIT("..");
      CR();
      */
    }
    break;

  case CMARK_NODE_BLOCK_QUOTE:
    if (entering) {
      CR();
      LIT(".RS");
      CR();
    } else {
      CR();
      LIT(".RE");
      CR();
    }
    break;

  case CMARK_NODE_LIST:
    break;

  case CMARK_NODE_ITEM:
    if (entering) {
      CR();
      LIT(".IP ");
      if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
        LIT("\\[bu] 2");
      } else {
        list_number = cmark_node_get_list_start(node->parent);
        tmp = node;
        while (tmp->prev) {
          tmp = tmp->prev;
          list_number += 1;
        }
        char list_number_s[LIST_NUMBER_SIZE];
        snprintf(list_number_s, LIST_NUMBER_SIZE, "\"%d.\" 4", list_number);
        LIT(list_number_s);
      }
      CR();
    } else {
      CR();
    }
    break;

  case CMARK_NODE_HEADING:
    if (entering) {
      CR();
      LIT(cmark_node_get_heading_level(node) == 1 ? ".SH" : ".SS");
      CR();
    } else {
      CR();
    }
    break;

  case CMARK_NODE_CODE_BLOCK:
    CR();
    LIT(".IP\n.nf\n\\f[C]\n");
    OUT(cmark_node_get_literal(node), false, NORMAL);
    CR();
    LIT("\\f[]\n.fi");
    CR();
    break;

  case CMARK_NODE_HTML_BLOCK:
    break;

  case CMARK_NODE_CUSTOM_BLOCK:
    CR();
    OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node),
        false, LITERAL);
    CR();
    break;

  case CMARK_NODE_THEMATIC_BREAK:
    CR();
    LIT(".PP\n  *  *  *  *  *");
    CR();
    break;

  case CMARK_NODE_PARAGRAPH:
    if (entering) {
      // no blank line if first paragraph in list:
      if (node->parent && node->parent->type == CMARK_NODE_ITEM &&
          node->prev == NULL) {
        // no blank line or .PP
      } else {
        CR();
        LIT(".PP");
        CR();
      }
    } else {
      CR();
    }
    break;

  case CMARK_NODE_TEXT:
    OUT(cmark_node_get_literal(node), allow_wrap, NORMAL);
    break;

  case CMARK_NODE_LINEBREAK:
    LIT(".PD 0\n.P\n.PD");
    CR();
    break;

  case CMARK_NODE_SOFTBREAK:
    if (options & CMARK_OPT_HARDBREAKS) {
      LIT(".PD 0\n.P\n.PD");
      CR();
    } else if (renderer->width == 0 && !(CMARK_OPT_NOBREAKS & options)) {
      CR();
    } else {
      OUT(" ", allow_wrap, LITERAL);
    }
    break;

  case CMARK_NODE_CODE:
    LIT("\\f[C]");
    OUT(cmark_node_get_literal(node), allow_wrap, NORMAL);
    LIT("\\f[]");
    break;

  case CMARK_NODE_HTML_INLINE:
    break;

  case CMARK_NODE_CUSTOM_INLINE:
    OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node),
        false, LITERAL);
    break;

  case CMARK_NODE_STRONG:
    if (entering) {
      LIT("\\f[B]");
    } else {
      LIT("\\f[]");
    }
    break;

  case CMARK_NODE_EMPH:
    if (entering) {
      LIT("\\f[I]");
    } else {
      LIT("\\f[]");
    }
    break;

  case CMARK_NODE_LINK:
    if (!entering) {
      LIT(" (");
      OUT(cmark_node_get_url(node), allow_wrap, URL);
      LIT(")");
    }
    break;

  case CMARK_NODE_IMAGE:
    if (entering) {
      LIT("[IMAGE: ");
    } else {
      LIT("]");
    }
    break;

  case CMARK_NODE_FOOTNOTE_DEFINITION:
  case CMARK_NODE_FOOTNOTE_REFERENCE:
    // TODO
    break;

  default:
    assert(false);
    break;
  }

  return 1;
}