static void findTxt2tagsTags (void) { NestingLevels *nls = nestingLevelsNew(); vString *name = vStringNew(); const unsigned char *line; while ((line = fileReadLine()) != NULL) { int depth; if (isTxt2tagsLine(line)) ; /* skip not to improperly match titles */ else if (parseTxt2tagsTitle(line, name, &depth)) { NestingLevel *nl = nestingLevelsGetCurrent(nls); while (nl && nl->indentation >= depth) { nestingLevelsPop(nls); nl = nestingLevelsGetCurrent(nls); } vStringTerminate(name); makeTxt2tagsTag(name, nls, K_SECTION); nestingLevelsPush(nls, name, K_SECTION); nestingLevelsGetCurrent(nls)->indentation = depth; } } vStringDelete (name); nestingLevelsFree(nls); }
/* TODO: parse overlining & underlining as distinct sections. */ static void findRstTags (void) { vString *name = vStringNew (); fpos_t filepos; const unsigned char *line; memset(&filepos, 0, sizeof(fpos_t)); memset(kindchars, 0, sizeof kindchars); nestingLevels = nestingLevelsNew(); while ((line = readLineFromInputFile ()) != NULL) { int line_len = strlen((const char*) line); int name_len_bytes = vStringLength(name); int name_len = utf8_strlen(vStringValue(name), name_len_bytes); /* if the name doesn't look like UTF-8, assume one-byte charset */ if (name_len < 0) name_len = name_len_bytes; /* underlines must be the same length or more */ if (line_len >= name_len && name_len > 0 && ispunct(line[0]) && issame((const char*) line)) { char c = line[0]; int kind = get_kind(c); if (kind >= 0) { makeRstTag(name, kind, filepos); continue; } } vStringClear (name); if (!isspace(*line)) { vStringCatS(name, (const char*)line); filepos = getInputFilePosition(); } vStringTerminate(name); } vStringDelete (name); nestingLevelsFree(nestingLevels); }
static void findRubyTags (void) { const unsigned char *line; boolean inMultiLineComment = FALSE; nesting = nestingLevelsNew (0); /* FIXME: this whole scheme is wrong, because Ruby isn't line-based. * You could perfectly well write: * * def * method * puts("hello") * end * * if you wished, and this function would fail to recognize anything. */ while ((line = readLineFromInputFile ()) != NULL) { const unsigned char *cp = line; /* if we expect a separator after a while, for, or until statement * separators are "do", ";" or newline */ boolean expect_separator = FALSE; if (canMatch (&cp, "=begin", isWhitespace)) { inMultiLineComment = TRUE; continue; } if (canMatch (&cp, "=end", isWhitespace)) { inMultiLineComment = FALSE; continue; } if (inMultiLineComment) continue; skipWhitespace (&cp); /* Avoid mistakenly starting a scope for modifiers such as * * return if <exp> * * FIXME: this is fooled by code such as * * result = if <exp> * <a> * else * <b> * end * * FIXME: we're also fooled if someone does something heinous such as * * puts("hello") \ * unless <exp> */ if (canMatchKeyword (&cp, "for") || canMatchKeyword (&cp, "until") || canMatchKeyword (&cp, "while")) { expect_separator = TRUE; enterUnnamedScope (); } else if (canMatchKeyword (&cp, "case") || canMatchKeyword (&cp, "if") || canMatchKeyword (&cp, "unless")) { enterUnnamedScope (); } /* * "module M", "class C" and "def m" should only be at the beginning * of a line. */ if (canMatchKeyword (&cp, "module")) { readAndEmitTag (&cp, K_MODULE); } else if (canMatchKeyword (&cp, "class")) { readAndEmitTag (&cp, K_CLASS); } else if (canMatchKeyword (&cp, "def")) { rubyKind kind = K_METHOD; NestingLevel *nl = nestingLevelsGetCurrent (nesting); tagEntryInfo *e = getEntryOfNestingLevel (nl); /* if the def is inside an unnamed scope at the class level, assume * it's from a singleton from a construct like this: * * class C * class << self * def singleton * ... * end * end * end */ if (e && (e->kind - RubyKinds) == K_CLASS && strlen (e->name) == 0) kind = K_SINGLETON; readAndEmitTag (&cp, kind); } while (*cp != '\0') { /* FIXME: we don't cope with here documents, * or regular expression literals, or ... you get the idea. * Hopefully, the restriction above that insists on seeing * definitions at the starts of lines should keep us out of * mischief. */ if (inMultiLineComment || isspace (*cp)) { ++cp; } else if (*cp == '#') { /* FIXME: this is wrong, but there *probably* won't be a * definition after an interpolated string (where # doesn't * mean 'comment'). */ break; } else if (canMatchKeyword (&cp, "begin")) { enterUnnamedScope (); } else if (canMatchKeyword (&cp, "do")) { if (! expect_separator) enterUnnamedScope (); else expect_separator = FALSE; } else if (canMatchKeyword (&cp, "end") && nesting->n > 0) { /* Leave the most recent scope. */ nestingLevelsPop (nesting); } else if (*cp == '"') { /* Skip string literals. * FIXME: should cope with escapes and interpolation. */ do { ++cp; } while (*cp != 0 && *cp != '"'); if (*cp == '"') cp++; /* skip the last found '"' */ } else if (*cp == ';') { ++cp; expect_separator = FALSE; } else if (*cp != '\0') { do ++cp; while (isIdentChar (*cp)); } } } nestingLevelsFree (nesting); }
static void findAsciidocTags(void) { vString *name = vStringNew(); const unsigned char *line; unsigned char in_block = '\0'; /* holds the block marking char or \0 if not in block */ nestingLevels = nestingLevelsNew(); while ((line = readLineFromInputFile()) != NULL) { int line_len = strlen((const char*) line); int name_len_bytes = vStringLength(name); int name_len = utf8_strlen(vStringValue(name), name_len_bytes); /* if the name doesn't look like UTF-8, assume one-byte charset */ if (name_len < 0) name_len = name_len_bytes; /* if its a title underline, or a delimited block marking character */ if (line[0] == '=' || line[0] == '-' || line[0] == '~' || line[0] == '^' || line[0] == '+' || line[0] == '.' || line[0] == '*' || line[0] == '_' || line[0] == '/') { int n_same; for (n_same = 1; line[n_same] == line[0]; ++n_same); /* is it a two line title or a delimited block */ if (n_same == line_len) { /* if in a block, can't be block start or title, look for block end */ if (in_block) { if (line[0] == in_block) in_block = '\0'; } /* if its a =_~^+ and the same length +-2 as the line before then its a title */ /* (except in the special case its a -- open block start line) */ else if ((line[0] == '=' || line[0] == '-' || line[0] == '~' || line[0] == '^' || line[0] == '+') && line_len <= name_len + 2 && line_len >= name_len - 2 && !(line_len == 2 && line[0] == '-')) { int kind = get_kind((char)(line[0])); if (kind >= 0) { makeAsciidocTag(name, kind); continue; } } /* else if its 4 or more /+-.*_= (plus the -- special case) its a block start */ else if (((line[0] == '/' || line[0] == '+' || line[0] == '-' || line[0] == '.' || line[0] == '*' || line[0] == '_' || line[0] == '=') && line_len >= 4 ) || (line[0] == '-' && line_len == 2)) { in_block = line[0]; } } /* otherwise is it a one line title */ else if (line[0] == '=' && n_same <= 5 && isspace(line[n_same]) && !in_block) { int kind = n_same - 1; int start = n_same; int end = line_len - 1; while (line[end] == line[0])--end; while (isspace(line[start]))++start; while (isspace(line[end]))--end; vStringClear(name); vStringNCatS(name, (const char*)(&(line[start])), end - start + 1); vStringTerminate(name); makeAsciidocTag(name, kind); continue; } } vStringClear(name); if (! isspace(*line)) vStringCatS(name, (const char*) line); vStringTerminate(name); } vStringDelete(name); nestingLevelsFree(nestingLevels); }