public override INode Consume(Parser parser, ParseData data, Lines lines, string scope) { var current = lines.Current(); var meta = lines.Value().Substring(10); var colDefs = meta.Split(':').Select(x => int.TryParse(x, out var v) ? v : 0).ToList(); var total = 0; foreach (var d in colDefs) { if (d > 0) { total += d; } else { lines.SetCurrent(current); return(null); } } if (total != 12) { lines.SetCurrent(current); return(null); } var i = 0; var arr = new List <string>(); var cols = new List <ColumnNode>(); while (lines.Next() && i < colDefs.Count) { var value = lines.Value().TrimEnd(); if (value == "%%") { cols.Add(new ColumnNode(colDefs[i], parser.ParseElements(data, String.Join("\n", arr), scope))); arr.Clear(); i++; } else { arr.Add(value); } if (i >= colDefs.Count) { break; } } if (i != colDefs.Count || arr.Count > 0) { lines.SetCurrent(current); return(null); } return(new HtmlNode("<div class=\"row\">", new NodeCollection(cols), "</div>")); }
public override INode Consume(Parser parser, ParseData data, Lines lines, string scope) { var current = lines.Current(); var line = lines.Value().Trim(); var res = Regex.Match(line, @"\[ref=([a-z ]+)\]", RegexOptions.IgnoreCase); if (!res.Success) { lines.SetCurrent(current); return(null); } var name = res.Groups[1].Value; var arr = new List <string>(); var found = false; while (lines.Next()) { var value = lines.Value().TrimEnd(); if (value == "[/ref]") { found = true; break; } else { arr.Add(value); } } if (!found || arr.Count == 0) { lines.SetCurrent(current); return(null); } // Trim blank lines from the start and end of the array for (var i = 0; i < 2; i++) { while (arr.Count > 0 && arr[0].Trim() == "") { arr.RemoveAt(0); } arr.Reverse(); } // Store the ref node var node = parser.ParseElements(data, string.Join("\n", arr), scope); data.Set($"Ref::{name}", node); // Return nothing return(PlainTextNode.Empty); }
public override INode Consume(Parser parser, ParseData data, Lines lines, string scope) { var current = lines.Current(); var firstLine = lines.Value().Substring(3).TrimEnd(); string lang = null; if (AllowedLanguages.Contains(firstLine, StringComparer.InvariantCultureIgnoreCase)) { lang = firstLine; firstLine = ""; } var arr = new List <string> { firstLine }; var found = false; while (lines.Next()) { var value = lines.Value().TrimEnd(); if (value.EndsWith("```")) { var lastLine = value.Substring(0, value.Length - 3); arr.Add(lastLine); found = true; break; } else { arr.Add(value); } } if (!found) { lines.SetCurrent(current); return(null); } // Trim blank lines from the start and end of the array for (var i = 0; i < 2; i++) { while (arr.Count > 0 && arr[0].Trim() == "") { arr.RemoveAt(0); } arr.Reverse(); } // Replace all tabs with 4 spaces arr = arr.Select(x => x.Replace("\t", " ")).ToList(); // Find the longest common whitespace amongst all lines (ignore blank lines) var longestWhitespace = arr.Aggregate(9999, (c, i) => { if (i.Trim().Length == 0) { return(c); } var wht = i.Length - i.TrimStart().Length; return(Math.Min(wht, c)); }); // Dedent all lines by the longest common whitespace arr = arr.Select(a => a.Substring(Math.Min(longestWhitespace, a.Length))).ToList(); var plain = new UnprocessablePlainTextNode(String.Join("\n", arr)); var cls = string.IsNullOrWhiteSpace(lang) ? "" : $" class=\"lang-{lang}\""; var before = $"<pre{cls}><code>"; var after = "</code></pre>"; return(new HtmlNode(before, plain, after)); }
public override INode Consume(Parser parser, ParseData data, Lines lines, string scope) { var current = lines.Current(); var meta = lines.Value().Substring(3); var title = ""; var found = false; var arr = new List <string>(); while (lines.Next()) { var value = lines.Value().TrimEnd(); if (value == "~~~") { found = true; break; } if (value.Length > 1 && value[0] == ':') { title = value.Substring(1).Trim(); } else { arr.Add(value); } } if (!found) { lines.SetCurrent(current); return(null); } string cls; if (meta == "message") { cls = "card-success"; } else if (meta == "info") { cls = "card-info"; } else if (meta == "warning") { cls = "card-warning"; } else if (meta == "error") { cls = "card-danger"; } else { cls = "card-default"; } var before = $"<div class=\"embed-panel card {cls}\">" + (title != "" ? $"<div class=\"card-header\">{System.Web.HttpUtility.HtmlEncode(title)}</div>" : "") + "<div class=\"card-body\">"; var content = parser.ParseElements(data, string.Join("\n", arr), scope); var after = "</div></div>"; return(new HtmlNode(before, content, after) { PlainBefore = title == "" ? "" : title + "\n" + new string('-', title.Length) + "\n" }); }
public override INode Consume(Parser parser, ParseData data, Lines lines, string scope) { var current = lines.Current(); var arr = new List <string>(); var line = lines.Value().Trim(); var res = Regex.Match(line, @"\[pre(?:=([a-z ]+))?\]", RegexOptions.IgnoreCase); if (!res.Success) { lines.SetCurrent(current); return(null); } line = line.Substring(0, res.Value.Length); string lang = null; var hl = false; if (res.Groups[1].Success) { var spl = res.Groups[1].Value.Split(' '); hl = spl.Contains("highlight"); lang = spl.FirstOrDefault(x => x != "highlight"); } if (line.EndsWith("[/pre]")) { lines.Next(); arr.Add(line.Substring(0, line.Length - 6)); } else { var found = false; while (lines.Next()) { var value = lines.Value().TrimEnd(); if (value.EndsWith("[/pre]")) { var lastLine = value.Substring(0, value.Length - 6); arr.Add(lastLine); found = true; break; } else { arr.Add(value); } } if (!found) { lines.SetCurrent(current); return(null); } } // Trim blank lines from the start and end of the array for (var i = 0; i < 2; i++) { while (arr.Count > 0 && arr[0].Trim() == "") { arr.RemoveAt(0); } arr.Reverse(); } // Process highlight commands var highlight = new List <(int firstLine, int numLines, string color)>(); if (hl) { // Highlight commands get their own line so we need to keep track of which lines we're removing as we go var newArr = new List <string>(); var firstLine = 0; foreach (var srcLine in arr) { if (srcLine.StartsWith("@@")) { var match = Regex.Match(srcLine, @"^@@(?:(#[0-9a-f]{3}|#[0-9a-f]{6}|[a-z]+|\d+)(?::(\d+))?)?$", RegexOptions.IgnoreCase | RegexOptions.Multiline); if (match.Success) { var numLines = 1; var color = "#FF8000"; for (var i = 1; i < match.Groups.Count; i++) { var p = match.Groups[i].Value; if (Colours.IsValidColor(p)) { color = p; } else if (int.TryParse(p, out var iv)) { numLines = iv; } } highlight.Add((firstLine, numLines, color)); continue; } } firstLine++; newArr.Add(srcLine); } arr = newArr; // Make sure highlights don't overlap each other or go past the end of the block highlight.Add((arr.Count, 0, "")); for (var i = 0; i < highlight.Count - 1; i++) { var(currFirst, currNum, currCol) = highlight[i]; var(nextFirst, _, _) = highlight[i + 1]; var lastLine = currFirst + currNum - 1; if (lastLine >= nextFirst) { highlight[i] = (currFirst, nextFirst - currFirst, currCol); } } highlight.RemoveAll(x => x.numLines <= 0); } // Replace all tabs with 4 spaces arr = arr.Select(x => x.Replace("\t", " ")).ToList(); // Find the longest common whitespace amongst all lines (ignore blank lines) var longestWhitespace = arr.Aggregate(9999, (c, i) => { if (i.Trim().Length == 0) { return(c); } var wht = i.Length - i.TrimStart().Length; return(Math.Min(wht, c)); }); // Dedent all lines by the longest common whitespace arr = arr.Select(a => a.Substring(Math.Min(longestWhitespace, a.Length))).ToList(); var highlights = string.Join("", highlight.Select( h => $"<div class=\"line-highlight\" style=\"top: {h.firstLine}em; height: {h.numLines}em; background: {h.color};\"></div>") ); var plain = new UnprocessablePlainTextNode(String.Join("\n", arr)); var cls = string.IsNullOrWhiteSpace(lang) ? "" : $" class=\"lang-{lang}\""; var before = $"<pre{cls}><code>{highlights}"; var after = "</code></pre>"; return(new HtmlNode(before, plain, after)); }