private string ProcessListItems(string list, string marker, bool isInsideParagraphlessListItem = false)
{
// The listLevel global keeps track of when we're inside a list.
// Each time we enter a list, we increment it; when we leave a list,
// we decrement. If it's zero, we're not in a list anymore.
// We do this because when we're not inside a list, we want to treat
// something like this:
// I recommend upgrading to version
// 8. Oops, now this line is treated
// as a sub-list.
// As a single paragraph, despite the fact that the second line starts
// with a digit-period-space sequence.
// Whereas when we're inside a list (or sub-list), that line will be
// treated as the start of a sub-list. What a kludge, huh? This is
// an aspect of Markdown's syntax that's hard to parse perfectly
// without resorting to mind-reading. Perhaps the solution is to
// change the syntax rules such that sub-lists must start with a
// starting cardinal number; e.g. "1." or "a.".
_listLevel++;
// Trim trailing blank lines:
list = Regex.Replace(list, @"\n{2,}\z", "\n");
string pattern = string.Format(
@"(^[ ]*) # leading whitespace = $1
({0}) [ ]+ # list marker = $2
((?s:.+?) # list item text = $3
(\n+))
(?= (\z | \1 ({0}) [ ]+))", marker);
bool lastItemHadADoubleNewline = false;
// has to be a closure, so subsequent invocations can share the bool
MatchEvaluator ListItemEvaluator = (Match match) =>
{
string item = match.Groups[3].Value;
bool endsWithDoubleNewline = item.EndsWith("\n\n");
bool containsDoubleNewline = endsWithDoubleNewline || item.Contains("\n\n");
if (containsDoubleNewline || lastItemHadADoubleNewline)
// we could correct any bad indentation here..
item = RunBlockGamut(Outdent(item) + "\n", unhash: false);
else
{
// recursion for sub-lists
item = DoLists(Outdent(item), isInsideParagraphlessListItem: true);
item = item.TrimEnd('\n');
if (!isInsideParagraphlessListItem) // only the outer-most item should run this, otherwise it's run multiple times for the inner ones
item = RunSpanGamut(item);
}
lastItemHadADoubleNewline = endsWithDoubleNewline;
return string.Format("<li>{0}</li>\n", item);
};
list = Regex.Replace(list, pattern, new MatchEvaluator(ListItemEvaluator),
RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline);
_listLevel--;
return list;
}