public static Syntax.Block ParseDocument(string commonMark, CommonMarkSettings settings = null) { using (var reader = new System.IO.StringReader(Helpers.Normalize(commonMark))) { return CommonMarkConverter.Parse(reader, settings); } }
private static Formatters.HtmlFormatter CreatePrintPlaceholdersWithColonDollarExcludesBHtmlFormatter(System.IO.TextWriter target, CommonMarkSettings stngs) { return new Formatters.HtmlFormatter(target, stngs) { PlaceholderResolver = placeholder => placeholder.Contains("b") ? null : ":" + placeholder + "$" }; }
static Markdown() { settings = CommonMarkSettings.Default.Clone(); settings.OutputFormat = OutputFormat.CustomDelegate; settings.OutputDelegate = (doc, output, settings) => new CustomHtmlFormatter(output, settings).WriteDocument(doc); }
public static void markdown2html(ApplicationContext context, ActiveEventArgs e) { // Making sure we clean up and remove all arguments passed in after execution using (new ArgsRemover(e.Args, false)) { // Assumes there's only one document, or creates one result of it. var md = XUtil.Single <string> (context, e.Args); // Making sure we correctly resolve URLs, if user specified a [root-url] argument. var root = e.Args.GetExChildValue("root-url", context, ""); CommonMarkSettings settings = CommonMarkSettings.Default; if (root != "") { // Unrolling path. root = context.RaiseEvent("p5.io.unroll-path", new Node("", root)).Get <string> (context); // To make sure we don't change global settings. settings = settings.Clone(); settings.UriResolver = delegate(string arg) { if (arg.StartsWithEx("http://") || arg.StartsWithEx("https://")) { return(arg); } return(root + arg.TrimStart('/')); }; } // Doing actual conversion. e.Args.Value = CommonMarkConverter.Convert(md, settings); } }
public MarkdownLoader(IHostingEnvironment environment, ILoggerFactory loggerFactory) { fileProvider = environment.ContentRootFileProvider; commonMarkSettings = CommonMarkSettings.Default.Clone(); bundles = new Dictionary <string, MarkdownBundle>(); // Lets us resolve numbers to issue links. commonMarkSettings.AdditionalFeatures = CommonMarkAdditionalFeatures.PlaceholderBracket; commonMarkSettings.OutputDelegate = FormatDocument; // May become obsolete, if we can resolve [NodaTime.LocalDateTime] to the right type link etc. commonMarkSettings.UriResolver = ResolveUrl; var logger = loggerFactory.CreateLogger <MarkdownLoader>(); var stopwatch = Stopwatch.StartNew(); // TODO: Make the root location configurable LoadBundleMetadata("Markdown"); PopulateParentBundles(); LoadBundleContent(); var totalPages = bundles.Values.SelectMany(b => b.Categories).Sum(c => c.Pages.Count); logger.LogInformation("Loaded {bundleCount} bundles totalling {pageCount} pages in {durationMs}ms", bundles.Count, totalPages, stopwatch.ElapsedMilliseconds); }
private MarkdownPage LoadPage(IFileProvider fileProvider, CommonMarkSettings commonMarkSettings, string id) { var filename = $"{ContentDirectory}/{id}.md"; try { var file = fileProvider.GetFileInfo(filename); if (!file.Exists) { if (ParentBundle == null) { throw new Exception($"Unable to find {file.Name} for bundle {Name} and no parent bundle exists"); } var page = ParentBundle.TryGetPage(id); if (page == null) { throw new Exception($"Unable to find {file.Name} for bundle {Name} and parent bundle doesn't have the page"); } return(page.WithBundle(this)); } using (var reader = file.CreateReader()) { return(MarkdownPage.Load(id, this, reader, commonMarkSettings)); } } catch (Exception e) { throw new Exception($"Unable to parse markdown content from {filename}", e); } }
/// <summary> /// Initializes the array of delegates for inline parsing. /// </summary> /// <returns></returns> internal static Func<Subject, Inline>[] InitializeParsers(CommonMarkSettings settings) { var strikethroughTilde = 0 != (settings.AdditionalFeatures & CommonMarkAdditionalFeatures.StrikethroughTilde); var placeholderBracket = 0 != (settings.AdditionalFeatures & CommonMarkAdditionalFeatures.PlaceholderBracket); var p = new Func<Subject, Inline>[strikethroughTilde ? 127 : 97]; p['\n'] = handle_newline; p['`'] = handle_backticks; p['\\'] = handle_backslash; p['&'] = HandleEntity; p['<'] = handle_pointy_brace; p['_'] = HandleEmphasis; p['*'] = HandleEmphasis; p['['] = HandleLeftSquareBracket; if (placeholderBracket) p[']'] = subj => HandleRightSquareBracket(subj, true); else p[']'] = subj => HandleRightSquareBracket(subj, false); p['!'] = HandleExclamation; if (strikethroughTilde) p['~'] = HandleTilde; return p; }
/// <summary> /// Initializes the array of delegates for inline parsing. /// </summary> /// <returns></returns> internal static Func <Subject, Inline>[] InitializeParsers(CommonMarkSettings settings) { var strikethroughTilde = 0 != (settings.AdditionalFeatures & CommonMarkAdditionalFeatures.StrikethroughTilde); var placeholderBracket = 0 != (settings.AdditionalFeatures & CommonMarkAdditionalFeatures.PlaceholderBracket); var p = new Func <Subject, Inline> [strikethroughTilde ? 127: 97]; p['\n'] = handle_newline; p['`'] = handle_backticks; p['\\'] = handle_backslash; p['&'] = HandleEntity; p['<'] = handle_pointy_brace; p['_'] = HandleEmphasis; p['*'] = HandleEmphasis; p['['] = HandleLeftSquareBracket; if (placeholderBracket) { p[']'] = subj => HandleRightSquareBracket(subj, true); } else { p[']'] = subj => HandleRightSquareBracket(subj, false); } p['!'] = HandleExclamation; if (strikethroughTilde) { p['~'] = HandleTilde; } return(p); }
/// <summary> /// Initializes the array of delegates for inline parsing. /// </summary> /// <returns></returns> internal static Func<Subject, Inline>[] InitializeParsers(CommonMarkSettings settings) { var singleCharTags = settings.InlineParserParameters.SingleCharTags; var doubleCharTags = settings.InlineParserParameters.DoubleCharTags; var length = singleCharTags.Length >= doubleCharTags.Length ? singleCharTags.Length : doubleCharTags.Length; var p = new Func<Subject, Inline>[length]; p['\n'] = handle_newline; p['`'] = handle_backticks; p['\\'] = handle_backslash; p['&'] = HandleEntity; p['<'] = handle_pointy_brace; p['['] = s => HandleLeftSquareBracket(s, settings); p[']'] = s => HandleRightSquareBracket(s, settings); p['!'] = s => HandleExclamation(s, settings); for (int i = 0; i < length; i++) { var singleCharTag = i < singleCharTags.Length ? singleCharTags[i] : (InlineTag)0; var doubleCharTag = i < doubleCharTags.Length ? doubleCharTags[i] : (InlineTag)0; if (singleCharTag != 0 || doubleCharTag != 0) p[i] = s => HandleOpenerCloser(s, singleCharTag, doubleCharTag, settings.InlineParserParameters); } return p; }
public static Syntax.Block ParseDocument(string commonMark, CommonMarkSettings settings = null) { using (var reader = new System.IO.StringReader(Helpers.Normalize(commonMark))) { return(CommonMarkConverter.Parse(reader, settings)); } }
static TelemetryNotice() { CommonMarkSettings = CommonMarkSettings.Default.Clone(); CommonMarkSettings.AdditionalFeatures |= CommonMarkAdditionalFeatures.PlaceholderBracket; CommonMarkSettings.UriResolver = UrlResolver; }
public static void ExecuteTest(string commonMark, string html, CommonMarkSettings settings = null, Func<System.IO.TextWriter, CommonMarkSettings, Formatters.HtmlFormatter> htmlFormatterFactory = null) { if (settings == null) settings = CommonMarkSettings.Default.Clone(); Helpers.LogValue("CommonMark", Denormalize(commonMark)); Helpers.LogValue("Expected", Denormalize(html)); // Arrange commonMark = Helpers.Normalize(commonMark); html = Helpers.Normalize(html); string actual; Syntax.Block document; // Act using (var reader = new System.IO.StringReader(commonMark)) using (var writer = new System.IO.StringWriter()) { if (htmlFormatterFactory != null) { settings = settings.Clone(); settings.OutputFormat = OutputFormat.CustomDelegate; settings.OutputDelegate = (doc, target, stngs) => htmlFormatterFactory(target, stngs).WriteDocument(doc); } document = CommonMarkConverter.ProcessStage1(reader, settings); CommonMarkConverter.ProcessStage2(document, settings); CommonMarkConverter.ProcessStage3(document, writer, settings); actual = writer.ToString(); } // Assert Helpers.LogValue("Actual", Denormalize(actual)); actual = Helpers.Tidy(actual); Assert.AreEqual(Helpers.Tidy(html), actual); if (htmlFormatterFactory == null) { // Verify that the extendable HTML formatter returns the same result, unless // the test was run with a custom extendable HTML formatter factory. In the // latter case, only the output of the custom extendable HTML formatter is // verified. var settingsHtmlFormatter = settings.Clone(); settingsHtmlFormatter.OutputDelegate = (doc, target, stngs) => new Formatters.HtmlFormatter(target, stngs).WriteDocument(doc); var actual2 = CommonMarkConverter.Convert(commonMark, settingsHtmlFormatter); Assert.AreEqual(actual, Helpers.Tidy(actual2), "HtmlFormatter returned a different result than HtmlFormatterSlim."); } // Additionally verify that the parser included source position information. // This is done here to catch cases during specification tests that might not be // covered in SourcePositionTests.cs. var firstFail = document.AsEnumerable().FirstOrDefault(o => o.Inline != null && o.IsOpening && o.Inline.SourceLength <= 0); if (firstFail != null) { Assert.Fail("Incorrect source position: " + firstFail); } }
internal void LoadContent(IFileProvider fileProvider, CommonMarkSettings commonMarkSettings) { foreach (var category in Categories) { category.Pages = category.PageIds.Select(id => LoadPage(fileProvider, commonMarkSettings, id)).ToList(); } resourcesById = Resources.ToDictionary(id => id, id => LoadResource(fileProvider, id)); pagesById = Categories.SelectMany(c => c.Pages).ToDictionary(p => p.Id); }
private void FormatDocument(Block block, TextWriter writer, CommonMarkSettings settings) { var formatter = new HtmlFormatter(writer, settings) { PlaceholderResolver = ResolvePlaceholder }; formatter.WriteDocument(block); }
/// <summary> /// Walk through the block, its children and siblings, parsing string content into inline content where appropriate. /// </summary> /// <param name="block">The document level block from which to start the processing.</param> /// <param name="data">Document data.</param> /// <param name="settings">The settings that influence how the inline parsing is performed.</param> public static void ProcessInlines(Block block, DocumentData data, CommonMarkSettings settings) { Stack <Inline> inlineStack = null; var stack = new Stack <Block>(); var parsers = settings.InlineParsers; var specialCharacters = settings.InlineParserSpecialCharacters; var subj = new Subject(data); StringContent sc; int delta; while (block != null) { var tag = block.Tag; if (tag == BlockTag.Paragraph || tag == BlockTag.AtxHeading || tag == BlockTag.SetextHeading) { sc = block.StringContent; if (sc != null) { sc.FillSubject(subj); delta = subj.Position; block.InlineContent = InlineMethods.parse_inlines(subj, parsers, specialCharacters); block.StringContent = null; if (sc.PositionTracker != null) { sc.PositionTracker.AddBlockOffset(-delta); AdjustInlineSourcePosition(block.InlineContent, sc.PositionTracker, ref inlineStack); } } } if (block.FirstChild != null) { if (block.NextSibling != null) { stack.Push(block.NextSibling); } block = block.FirstChild; } else if (block.NextSibling != null) { block = block.NextSibling; } else if (stack.Count > 0) { block = stack.Pop(); } else { block = null; } } }
/// <summary>Initializes a new instance of the <see cref="HtmlFormatter" /> class.</summary> /// <param name="target">The target text writer.</param> /// <param name="settings">The settings used when formatting the data.</param> /// <exception cref="ArgumentNullException">when <paramref name="target"/> is <see langword="null"/></exception> public HtmlFormatter(TextWriter target, CommonMarkSettings settings) { if (target == null) throw new ArgumentNullException(nameof(target)); if (settings == null) settings = CommonMarkSettings.Default; _target = new HtmlTextWriter(target); _settings = settings; }
internal static InlineTag[] InitializeDoubleCharTags(CommonMarkSettings settings) { var handleTilde = 0 != (settings.AdditionalFeatures & CommonMarkAdditionalFeatures.StrikethroughTilde); var t = InitializeEmphasisDoubleCharTags(handleTilde); if (handleTilde) t['~'] = InlineTag.Strikethrough; return t; }
public static async Task <string> MarkdownToHTML(string markdownCode) { string htmlCode = null; await Task.Run(() => { CommonMarkSettings settings = CommonMarkSettings.Default; settings.OutputFormat = OutputFormat.Html; settings.AdditionalFeatures = CommonMarkAdditionalFeatures.All; htmlCode = CommonMarkConverter.Convert(source: markdownCode, settings: settings); }); return(htmlCode); }
public async Task <string> ConvertToHtml(string mdFile, Func <string, string> resolveUri) { CommonMarkSettings settings = null; if (resolveUri != null) { settings = CommonMarkSettings.Default.Clone(); settings.UriResolver = resolveUri; } return(CommonMarkConverter.Convert(mdFile, settings)); }
static SemanticReleaseNotesFormatter() { DefaultCommonMarkSettings = CommonMarkSettings.Default.Clone(); DefaultCommonMarkSettings.AdditionalFeatures = CommonMarkAdditionalFeatures.StrikethroughTilde; DefaultCommonMarkSettings.OutputFormat = CommonMark.OutputFormat.Html; DefaultCommonMarkSettings.OutputDelegate = (doc, output, settings) => new SemanticReleaseNotesHtmlFormatter(output, settings).WriteDocument(doc); Template.RegisterSafeType(typeof(Metadata), new[] { "Name", "Value" }); Template.RegisterSafeType(typeof(ReleaseNotes), new[] { "Summary", "Sections", "Items", "Metadata" }); Template.RegisterSafeType(typeof(Section), new[] { "Name", "Summary", "Items", "Icon" }); Template.RegisterSafeType(typeof(Category), new[] { "Name", "Items" }); Template.RegisterSafeType(typeof(Item), new[] { "TaskId", "TaskLink", "Categories", "Priority", "Summary" }); }
public static void ExecuteTest(string commonMark, string html, CommonMarkSettings settings = null) { if (settings == null) { settings = CommonMarkSettings.Default.Clone(); } Helpers.LogValue("CommonMark", Denormalize(commonMark)); Helpers.LogValue("Expected", Denormalize(html)); // Arrange commonMark = Helpers.Normalize(commonMark); html = Helpers.Normalize(html); string actual; Syntax.Block document; // Act using (var reader = new System.IO.StringReader(commonMark)) using (var writer = new System.IO.StringWriter()) { document = CommonMarkConverter.ProcessStage1(reader, settings); CommonMarkConverter.ProcessStage2(document, settings); CommonMarkConverter.ProcessStage3(document, writer, settings); actual = writer.ToString(); } // Assert Helpers.LogValue("Actual", Denormalize(actual)); actual = Helpers.Tidy(actual); Assert.AreEqual(Helpers.Tidy(html), actual); // Verify that the extendable HTML formatter returns the same result var settingsHtmlFormatter = settings.Clone(); settingsHtmlFormatter.OutputDelegate = (doc, target, stngs) => new Formatters.HtmlFormatter(target, stngs).WriteDocument(doc); var actual2 = CommonMarkConverter.Convert(commonMark, settingsHtmlFormatter); Assert.AreEqual(actual, Helpers.Tidy(actual2), "HtmlFormatter returned a different result than HtmlFormatterSlim."); // Additionally verify that the parser included source position information. // This is done here to catch cases during specification tests that might not be // covered in SourcePositionTests.cs. var firstFail = document.AsEnumerable().FirstOrDefault(o => o.Inline != null && o.IsOpening && o.Inline.SourceLength <= 0); if (firstFail != null) { Assert.Fail("Incorrect source position: " + firstFail); } }
public override byte[] ReadBytes() { if (CachedContent.Length == 0) { Refresh(); } string text = System.Text.Encoding.UTF8.GetString(CachedContent); CommonMarkSettings setting = CommonMarkSettings.Default.Clone(); setting.AdditionalFeatures = CommonMarkAdditionalFeatures.All; html = CommonMarkConverter.Convert(text, setting); return(System.Text.Encoding.UTF8.GetBytes(html)); }
public FlowDocument BlocksToXaml(Block block, CommonMarkSettings settings) { //_checkBoxNumber = 0; FlowDocument document = new FlowDocument(); document.PagePadding = new Thickness(0); if (DocumentStyle != null) { document.Style = DocumentStyle; } BlocksToXamlInner(document, block, settings); return(document); }
public MarkdownLoader(IHostingEnvironment environment) { fileProvider = environment.ContentRootFileProvider; commonMarkSettings = CommonMarkSettings.Default.Clone(); bundles = new Dictionary <string, MarkdownBundle>(); // Lets us resolve numbers to issue links. commonMarkSettings.AdditionalFeatures = CommonMarkAdditionalFeatures.PlaceholderBracket; commonMarkSettings.OutputDelegate = FormatDocument; // May become obsolete, if we can resolve [NodaTime.LocalDateTime] to the right type link etc. commonMarkSettings.UriResolver = ResolveUrl; // TODO: Make the root location configurable LoadRecursive("Markdown"); }
/// <summary>Initializes a new instance of the <see cref="HtmlFormatter" /> class.</summary> /// <param name="target">The target text writer.</param> /// <param name="settings">The settings used when formatting the data.</param> /// <exception cref="ArgumentNullException">when <paramref name="target"/> is <c>null</c></exception> public HtmlFormatter(TextWriter target, CommonMarkSettings settings) { if (target == null) { throw new ArgumentNullException(target.ToString()); } if (settings == null) { settings = CommonMarkSettings.Default; } _target = new HtmlTextWriter(target); _settings = settings; }
/// <summary> /// Initializes a new instance of <see cref="ProjbookHtmlFormatter"/>. /// </summary> /// <param name="contextName">Initializes the required <see cref="ContextName"/></param> /// <param name="target">Initializes the required text writer used as output.</param> /// <param name="settings">Initializes the required common mark settings used by the formatting.</param> /// <param name="sectionTitleBase">Initializes the section title base.</param> /// <param name="snippetDictionary">Initializes the snippet directory.</param> /// <param name="snippetReferencePrefix">Initializes the snippet reference prefix.</param> public ProjbookHtmlFormatter(string contextName, TextWriter target, CommonMarkSettings settings, int sectionTitleBase, Dictionary <Guid, Extension.Model.Snippet> snippetDictionary, string snippetReferencePrefix) : base(target, settings) { // Data validation Ensure.That(() => contextName).IsNotNullOrWhiteSpace(); Ensure.That(target is StreamWriter).IsTrue(); Ensure.That(() => sectionTitleBase).IsGte(0); Ensure.That(() => snippetDictionary).IsNotNull(); Ensure.That(() => snippetReferencePrefix).IsNotNull(); // Initialize this.ContextName = contextName; this.pageBreak = new List <PageBreakInfo>(); this.writer = target as StreamWriter; this.sectionTitleBase = sectionTitleBase; this.snippetDictionary = snippetDictionary; this.snippetReferencePrefix = snippetReferencePrefix; }
internal static InlineTag[] InitializeSingleCharTags(CommonMarkSettings settings) { var handleCaret = 0 != (settings.AdditionalFeatures & CommonMarkAdditionalFeatures.SuperscriptCaret); var handleTilde = 0 != (settings.AdditionalFeatures & CommonMarkAdditionalFeatures.SubscriptTilde); var handleMath = 0 != (settings.AdditionalFeatures & CommonMarkAdditionalFeatures.MathDollar); var t = InitializeEmphasisSingleCharTags(handleTilde); if (handleCaret) t['^'] = InlineTag.Superscript; if (handleTilde) t['~'] = InlineTag.Subscript; if (handleMath) t['$'] = InlineTag.Math; return t; }
/// <summary> /// Break out of all containing lists /// </summary> private static void BreakOutOfLists(ref Block blockRef, LineInfo line, CommonMarkSettings settings) { Block container = blockRef; Block b = container.Top; // find first containing list: while (b != null && b.Tag != BlockTag.List) b = b.LastChild; if (b != null) { while (container != null && container != b) { Finalize(container, line, settings); container = container.Parent; } Finalize(b, line, settings); blockRef = b.Parent; } }
public static void ExecuteTest(string commonMark, string html, CommonMarkSettings settings = null) { if (settings == null) settings = CommonMarkSettings.Default.Clone(); Helpers.LogValue("CommonMark", Denormalize(commonMark)); Helpers.LogValue("Expected", Denormalize(html)); // Arrange commonMark = Helpers.Normalize(commonMark); html = Helpers.Normalize(html); string actual; Syntax.Block document; // Act using (var reader = new System.IO.StringReader(commonMark)) using (var writer = new System.IO.StringWriter()) { document = CommonMarkConverter.ProcessStage1(reader, settings); CommonMarkConverter.ProcessStage2(document, settings); CommonMarkConverter.ProcessStage3(document, writer, settings); actual = writer.ToString(); } // Assert Helpers.LogValue("Actual", Denormalize(actual)); actual = Helpers.Tidy(actual); Assert.AreEqual(Helpers.Tidy(html), actual); // Verify that the parser included source position information. // This is done here to catch cases during specification tests that might not be // covered in SourcePositionTests.cs. var firstFail = document.AsEnumerable().FirstOrDefault(o => o.Inline != null && o.IsOpening && o.Inline.SourceLength <= 0); if (firstFail != null) { Assert.Fail("Incorrect source position: " + firstFail); } }
public static void ExecuteTest(string commonMark, string html, CommonMarkSettings settings = null) { Helpers.LogValue("CommonMark", Denormalize(commonMark)); Helpers.LogValue("Expected", Denormalize(html)); // Arrange commonMark = Helpers.Normalize(commonMark); html = Helpers.Normalize(html); string actual; Syntax.Block document; // Act using (var reader = new System.IO.StringReader(commonMark)) using (var writer = new System.IO.StringWriter()) { document = CommonMarkConverter.ProcessStage1(reader, settings); CommonMarkConverter.ProcessStage2(document, settings); CommonMarkConverter.ProcessStage3(document, writer, settings); actual = writer.ToString(); } // Assert Helpers.LogValue("Actual", Denormalize(actual)); Assert.AreEqual(Helpers.Tidy(html), Helpers.Tidy(actual)); // Additionally verify that the parser included source position information. // This is done here to catch cases during specification tests that might not be // covered in SourcePositionTests.cs. var firstFail = document.AsEnumerable().FirstOrDefault(o => o.Inline != null && o.IsOpening && o.Inline.SourceLength <= 0); if (firstFail != null) { Assert.Fail("Incorrect source position: " + firstFail); } }
/// <summary> /// Adds a new reference to the dictionary, if the label does not already exist there. /// Assumes that the length of the label does not exceed <see cref="Reference.MaximumReferenceLabelLength"/>. /// </summary> private static void AddReference(Dictionary<string, Reference> refmap, StringPart label, string url, string title, CommonMarkSettings settings) { var normalizedLabel = NormalizeReference(label, settings); if (refmap.ContainsKey(normalizedLabel)) return; refmap.Add(normalizedLabel, new Reference(normalizedLabel, url, title)); }
/// <summary> /// Writes the inline list to the given parent as HTML code. /// </summary> private void InlinesToXaml(IAddChild parent, Inline inline, CommonMarkSettings settings, Stack <InlineStackEntry> stack) { var uriResolver = settings.UriResolver; bool withinLink = false; bool stackWithinLink = false; bool trackPositions = settings.TrackSourcePosition; IAddChild blockParent = parent; if (blockParent is ListItem || blockParent is Section || blockParent is TableCell) { Paragraph p = new Paragraph(); blockParent.AddChild(p); blockParent = p; } while (inline != null) { var visitChildren = false; IAddChild lastParent = null; switch (inline.Tag) { case InlineTag.String: //if (inline.LiteralContent.StartsWith("[ ]") || inline.LiteralContent.StartsWith("[x]")) //{ // CheckBox bt = new CheckBox // { // IsChecked = inline.LiteralContent.Contains("[x]"), // Content = inline.LiteralContent.Substring(2), // Tag = _checkBoxNumber // }; // bt.CommandParameter = bt; // bt.Command = CheckBoxCheckedCommand; // bt.Style = TodoCheckBoxStyle; // blockParent.AddChild(new BlockUIContainer(bt)); // _checkBoxNumber++; //} //else blockParent.AddText(inline.LiteralContent); break; case InlineTag.LineBreak: blockParent.AddChild(new LineBreak()); break; case InlineTag.SoftBreak: if (settings.RenderSoftLineBreaksAsLineBreaks) { blockParent.AddChild(new LineBreak()); } break; case InlineTag.Code: Span codeSpan = new Span(new Run(inline.LiteralContent)); if (InlineCodeStyle != null) { codeSpan.Style = InlineCodeStyle; } blockParent.AddChild(codeSpan); break; case InlineTag.RawHtml: // cannot output source position for HTML blocks blockParent.AddText(inline.LiteralContent); break; case InlineTag.Link: if (withinLink) { //parent.Write('['); //stackLiteral = "]"; stackWithinLink = true; visitChildren = true; } else { Hyperlink hyperlink = new Hyperlink(); if (LinkStyle != null) { hyperlink.Style = LinkStyle; } string url = inline.TargetUrl; if (uriResolver != null) { url = uriResolver(inline.TargetUrl); } hyperlink.CommandParameter = url; if (Uri.IsWellFormedUriString(url, UriKind.Absolute)) { hyperlink.NavigateUri = new Uri(url); hyperlink.RequestNavigate += (sender, e) => { System.Diagnostics.Process.Start(e.Uri.ToString()); }; } else { hyperlink.Command = HyperlinkCommand; } if (inline.LiteralContent.Length > 0) { hyperlink.ToolTip = inline.LiteralContent; } if (trackPositions) { PrintPosition(hyperlink, inline); } if (!(blockParent is Hyperlink)) { blockParent.AddChild(hyperlink); } lastParent = blockParent; blockParent = hyperlink; visitChildren = true; stackWithinLink = true; } break; case InlineTag.Image: HandleImage(inline, parent); break; case InlineTag.Strong: Bold bold = new Bold(); blockParent.AddChild(bold); lastParent = blockParent; blockParent = bold; if (trackPositions) { PrintPosition(bold, inline); } stackWithinLink = withinLink; visitChildren = true; break; case InlineTag.Emphasis: Italic italic = new Italic(); blockParent.AddChild(italic); lastParent = blockParent; blockParent = italic; if (trackPositions) { PrintPosition(italic, inline); } visitChildren = true; stackWithinLink = withinLink; break; case InlineTag.Strikethrough: Span strikethroughSpan = new Span(); strikethroughSpan.TextDecorations = TextDecorations.Strikethrough; blockParent.AddChild(strikethroughSpan); lastParent = blockParent; blockParent = strikethroughSpan; if (trackPositions) { PrintPosition(strikethroughSpan, inline); } visitChildren = true; stackWithinLink = withinLink; break; case InlineTag.Placeholder: // the slim formatter will treat placeholders like literals, without applying any further processing //if (blockParent is ListItem) // blockParent.AddChild(new Paragraph(new Run("Placeholder"))); //else // blockParent.AddText("Placeholder"); //visitChildren = false; //TODO: Handle todo-list items here break; default: throw new CommonMarkException("Inline type " + inline.Tag + " is not supported.", inline); } if (visitChildren) { stack.Push(new InlineStackEntry(lastParent, inline.NextSibling, withinLink)); withinLink = stackWithinLink; inline = inline.FirstChild; } else if (inline.NextSibling != null) { inline = inline.NextSibling; } else { inline = null; } while (inline == null && stack.Count > 0) { var entry = stack.Pop(); blockParent = entry.Parent; inline = entry.Target; withinLink = entry.IsWithinLink; } } }
public static void Finalize(Block b, LineInfo line, CommonMarkSettings settings) { // don't do anything if the block is already closed if (!b.IsOpen) return; b.IsOpen = false; if (line.IsTrackingPositions) { // (b.SourcePosition >= line.LineOffset) determines if the block started on this line. if (b.SourcePosition >= line.LineOffset && line.Line != null) b.SourceLastPosition = line.CalculateOrigin(line.Line.Length, false); else b.SourceLastPosition = line.CalculateOrigin(0, false); } #pragma warning disable 0618 b.EndLine = (line.LineNumber > b.StartLine) ? line.LineNumber - 1 : line.LineNumber; #pragma warning restore 0618 switch (b.Tag) { case BlockTag.Paragraph: var sc = b.StringContent; if (!sc.StartsWith('[')) break; var subj = new Subject(b.Top.ReferenceMap); sc.FillSubject(subj); var origPos = subj.Position; while (subj.Position < subj.Buffer.Length && subj.Buffer[subj.Position] == '[' && 0 != InlineMethods.ParseReference(subj, settings)) { } if (subj.Position != origPos) { sc.Replace(subj.Buffer, subj.Position, subj.Buffer.Length - subj.Position); if (sc.PositionTracker != null) sc.PositionTracker.AddBlockOffset(subj.Position - origPos); if (Utilities.IsFirstLineBlank(subj.Buffer, subj.Position)) b.Tag = BlockTag.ReferenceDefinition; } break; case BlockTag.IndentedCode: b.StringContent.RemoveTrailingBlankLines(); break; case BlockTag.FencedCode: case BlockTag.CustomContainer: // first line of contents becomes info var firstlinelen = b.StringContent.IndexOf('\n') + 1; b.FencedCodeData.Info = InlineMethods.Unescape(b.StringContent.TakeFromStart(firstlinelen, true).Trim()); break; case BlockTag.List: // determine tight/loose status b.ListData.IsTight = true; // tight by default var item = b.FirstChild; Block subitem; while (item != null) { // check for non-final non-empty list item ending with blank line: if (item.IsLastLineBlank && item.NextSibling != null) { b.ListData.IsTight = false; break; } // recurse into children of list item, to see if there are spaces between them: subitem = item.FirstChild; while (subitem != null) { if (EndsWithBlankLine(subitem) && (item.NextSibling != null || subitem.NextSibling != null)) { b.ListData.IsTight = false; break; } subitem = subitem.NextSibling; } if (!b.ListData.IsTight) break; item = item.NextSibling; } break; } }
private void WriteGitHubHeadingIds(HtmlTextWriter writer, Inline inline, CommonMarkSettings settings, Stack<InlineStackEntry> stack) { using (var tempWriter = new System.IO.StringWriter()) { var tempWrapper = new HtmlTextWriter(tempWriter); var tempStringBuilder = new StringBuilder(); InlinesToPlainText(tempWrapper, inline, stack); string plaintextContent = tempWriter.ToString(); // Normalize plaintext content according to GitHub ID rules plaintextContent = new Regex(@"[^\w\-\ ]").Replace(plaintextContent, "").Replace(" ", "-"); for (int c = 0; c < plaintextContent.Length; c++) { if (plaintextContent[c] >= 'A' && plaintextContent[c] <= 'Z') tempStringBuilder.Append((char)(plaintextContent[c] + 32)); else tempStringBuilder.Append(plaintextContent[c]); } plaintextContent = tempStringBuilder.ToString(); string unique = ""; if (GitHubHeaderIdCounts.ContainsKey(plaintextContent)) unique = "-" + ++GitHubHeaderIdCounts[plaintextContent]; else GitHubHeaderIdCounts[plaintextContent] = 0; writer.WriteConstant(string.Format(@"<a id=""user-content-{0}{1}"" class=""anchor"" href=""#user-content-{0}{1}"" aria-hidden=""true""><svg class=""octicon octicon-link"" aria-hidden=""true"" height=""16"" role=""img"" version=""1.1"" viewBox=""0 0 16 16"" width=""16""><path d=""M4 9h1v1h-1c-1.5 0-3-1.69-3-3.5s1.55-3.5 3-3.5h4c1.45 0 3 1.69 3 3.5 0 1.41-0.91 2.72-2 3.25v-1.16c0.58-0.45 1-1.27 1-2.09 0-1.28-1.02-2.5-2-2.5H4c-0.98 0-2 1.22-2 2.5s1 2.5 2 2.5z m9-3h-1v1h1c1 0 2 1.22 2 2.5s-1.02 2.5-2 2.5H9c-0.98 0-2-1.22-2-2.5 0-0.83 0.42-1.64 1-2.09v-1.16c-1.09 0.53-2 1.84-2 3.25 0 1.81 1.55 3.5 3 3.5h4c1.45 0 3-1.69 3-3.5s-1.5-3.5-3-3.5z""></path></svg></a>", plaintextContent, unique)); } }
/// <summary> /// Writes the inline list to the given writer as HTML code. /// </summary> private static void InlinesToHtml(HtmlTextWriter writer, Inline inline, CommonMarkSettings settings, Stack<InlineStackEntry> stack) { var uriResolver = settings.UriResolver; bool withinLink = false; bool stackWithinLink = false; bool visitChildren; bool trackPositions = settings.TrackSourcePosition; string stackLiteral = null; while (inline != null) { visitChildren = false; switch (inline.Tag) { case InlineTag.String: if (trackPositions) { writer.WriteConstant("<span"); PrintPosition(writer, inline); writer.Write('>'); EscapeHtml(inline.LiteralContentValue, writer); writer.WriteConstant("</span>"); } else { EscapeHtml(inline.LiteralContentValue, writer); } break; case InlineTag.LineBreak: writer.WriteLineConstant("<br />"); break; case InlineTag.SoftBreak: if (settings.RenderSoftLineBreaksAsLineBreaks) writer.WriteLineConstant("<br />"); else writer.WriteLine(); break; case InlineTag.Code: writer.WriteConstant("<code"); if (trackPositions) PrintPosition(writer, inline); writer.Write('>'); EscapeHtml(inline.LiteralContentValue, writer); writer.WriteConstant("</code>"); break; case InlineTag.RawHtml: // cannot output source position for HTML blocks writer.Write(inline.LiteralContentValue); break; case InlineTag.Link: if (withinLink) { writer.Write('['); stackLiteral = "]"; stackWithinLink = true; visitChildren = true; } else { writer.WriteConstant("<a href=\""); if (uriResolver != null) EscapeUrl(uriResolver(inline.TargetUrl), writer); else EscapeUrl(inline.TargetUrl, writer); writer.Write('\"'); if (inline.LiteralContentValue.Length > 0) { writer.WriteConstant(" title=\""); EscapeHtml(inline.LiteralContentValue, writer); writer.Write('\"'); } if (trackPositions) PrintPosition(writer, inline); writer.Write('>'); visitChildren = true; stackWithinLink = true; stackLiteral = "</a>"; } break; case InlineTag.Image: writer.WriteConstant("<img src=\""); if (uriResolver != null) EscapeUrl(uriResolver(inline.TargetUrl), writer); else EscapeUrl(inline.TargetUrl, writer); writer.WriteConstant("\" alt=\""); InlinesToPlainText(writer, inline.FirstChild, stack); writer.Write('\"'); if (inline.LiteralContentValue.Length > 0) { writer.WriteConstant(" title=\""); EscapeHtml(inline.LiteralContentValue, writer); writer.Write('\"'); } if (trackPositions) PrintPosition(writer, inline); writer.WriteConstant(" />"); break; case InlineTag.Strong: writer.WriteConstant("<strong"); if (trackPositions) PrintPosition(writer, inline); writer.Write('>'); stackLiteral = "</strong>"; stackWithinLink = withinLink; visitChildren = true; break; case InlineTag.Emphasis: writer.WriteConstant("<em"); if (trackPositions) PrintPosition(writer, inline); writer.Write('>'); stackLiteral = "</em>"; visitChildren = true; stackWithinLink = withinLink; break; case InlineTag.Strikethrough: writer.WriteConstant("<del"); if (trackPositions) PrintPosition(writer, inline); writer.Write('>'); stackLiteral = "</del>"; visitChildren = true; stackWithinLink = withinLink; break; default: throw new CommonMarkException("Inline type " + inline.Tag + " is not supported.", inline); } if (visitChildren) { stack.Push(new InlineStackEntry(stackLiteral, inline.NextSibling, withinLink)); withinLink = stackWithinLink; inline = inline.FirstChild; } else if (inline.NextSibling != null) { inline = inline.NextSibling; } else { inline = null; } while (inline == null && stack.Count > 0) { var entry = stack.Pop(); writer.WriteConstant(entry.Literal); inline = entry.Target; withinLink = entry.IsWithinLink; } } }
public ExternalLinkFormatter(System.IO.TextWriter target, CommonMarkSettings settings) : base(target, settings) { }
private static Inline HandleRightSquareBracket(Subject subj, CommonMarkSettings settings) { // move past ']' subj.Position++; bool canClose; var istack = InlineStack.FindMatchingOpener(subj.LastPendingInline, InlineStack.InlineStackPriority.Links, '[', out canClose); if (istack != null) { // if the opener is "inactive" then it means that there was a nested link if (istack.DelimeterCount == -1) { InlineStack.RemoveStackEntry(istack, subj, istack, settings.InlineParserParameters); return new Inline("]", subj.Position - 1, subj.Position); } var endpos = subj.Position; // try parsing details for '[foo](/url "title")' or '[foo][bar]' var details = ParseLinkDetails(subj, settings); // try lookup of the brackets themselves if (details == null || details == Reference.SelfReference) { var startpos = istack.StartPosition; var label = new StringPart(subj.Buffer, startpos, endpos - startpos - 1); details = LookupReference(subj.ReferenceMap, label, settings); } if (details == Reference.InvalidReference) details = null; MatchSquareBracketStack(istack, subj, details, settings.InlineParserParameters); return null; } var inlText = new Inline("]", subj.Position - 1, subj.Position); if (canClose) { // note that the current implementation will not work if there are other inlines with priority // higher than Links. // to fix this the parsed link details should be added to the closer element in the stack. throw new NotSupportedException("It is not supported to have inline stack priority higher than Links."); ////istack = new InlineStack(); ////istack.Delimeter = '['; ////istack.StartingInline = inlText; ////istack.StartPosition = subj.Position; ////istack.Priority = InlineStack.InlineStackPriority.Links; ////istack.Flags = InlineStack.InlineStackFlags.Closer; ////InlineStack.AppendStackEntry(istack, subj); } return inlText; }
/// <summary> /// Convert a block list to HTML. Returns 0 on success, and sets result. /// </summary> /// <remarks><para>Orig: blocks_to_html.</para></remarks> public static void BlocksToHtml(System.IO.TextWriter writer, Block block, CommonMarkSettings settings, ISymbol documentedSymbol) { var wrapper = new DocumentationCommentTextWriter(writer); BlocksToHtmlInner(wrapper, block, settings, documentedSymbol); }
private static Inline HandleLeftSquareBracket(Subject subj, bool isImage, CommonMarkSettings settings) { Inline inlText; if (isImage) { inlText = new Inline("![", subj.Position - 1, subj.Position + 1); } else { inlText = new Inline("[", subj.Position, subj.Position + 1); } // move past the '[' subj.Position++; var istack = new InlineStack(); istack.Delimeter = '['; istack.StartingInline = inlText; istack.StartPosition = subj.Position; istack.Priority = InlineStack.InlineStackPriority.Links; istack.Flags = InlineStack.InlineStackFlags.Opener | (isImage ? InlineStack.InlineStackFlags.ImageLink : InlineStack.InlineStackFlags.None); InlineStack.AppendStackEntry(istack, subj); return inlText; }
/// <summary> /// Write the block data to the given writer. /// </summary> public static void PrintBlocks(TextWriter writer, Block block, CommonMarkSettings settings) { int indent = 0; var stack = new Stack<BlockStackEntry>(); var inlineStack = new Stack<InlineStackEntry>(); var buffer = new StringBuilder(); var trackPositions = settings.TrackSourcePosition; while (block != null) { writer.Write(new string(' ', indent)); switch (block.Tag) { case BlockTag.Document: writer.Write("document"); PrintPosition(trackPositions, writer, block); break; case BlockTag.BlockQuote: writer.Write("block_quote"); PrintPosition(trackPositions, writer, block); break; case BlockTag.ListItem: writer.Write("list_item"); PrintPosition(trackPositions, writer, block); break; case BlockTag.List: writer.Write("list"); PrintPosition(trackPositions, writer, block); var data = block.ListData; if (data.ListType == ListType.Ordered) { writer.Write(" (type=ordered tight={0} start={1} delim={2})", data.IsTight, data.Start, data.Delimiter); } else { writer.Write("(type=bullet tight={0} bullet_char={1})", data.IsTight, data.BulletChar); } break; case BlockTag.AtxHeader: writer.Write("atx_header"); PrintPosition(trackPositions, writer, block); writer.Write(" (level={0})", block.HeaderLevel); break; case BlockTag.SETextHeader: writer.Write("setext_header"); PrintPosition(trackPositions, writer, block); writer.Write(" (level={0})", block.HeaderLevel); break; case BlockTag.Paragraph: writer.Write("paragraph"); PrintPosition(trackPositions, writer, block); break; case BlockTag.HorizontalRuler: writer.Write("hrule"); PrintPosition(trackPositions, writer, block); break; case BlockTag.IndentedCode: writer.Write("indented_code {0}", format_str(block.StringContent.ToString(buffer), buffer)); PrintPosition(trackPositions, writer, block); writer.Write(' '); writer.Write(format_str(block.StringContent.ToString(buffer), buffer)); break; case BlockTag.FencedCode: writer.Write("fenced_code"); PrintPosition(trackPositions, writer, block); writer.Write(" length={0} info={1} {2}", block.FencedCodeData.FenceLength, format_str(block.FencedCodeData.Info, buffer), format_str(block.StringContent.ToString(buffer), buffer)); break; case BlockTag.CustomContainer: writer.Write("custom_container"); PrintPosition(trackPositions, writer, block); writer.Write(" length={0} info={1} {2}", block.FencedCodeData.FenceLength, format_str(block.FencedCodeData.Info, buffer), format_str(block.StringContent.ToString(buffer), buffer)); break; case BlockTag.HtmlBlock: writer.Write("html_block"); PrintPosition(trackPositions, writer, block); writer.Write(' '); writer.Write(format_str(block.StringContent.ToString(buffer), buffer)); break; case BlockTag.ReferenceDefinition: writer.Write("reference_def"); PrintPosition(trackPositions, writer, block); break; default: throw new CommonMarkException("Block type " + block.Tag + " is not supported.", block); } writer.WriteLine(); if (block.InlineContent != null) { PrintInlines(writer, block.InlineContent, indent + 2, inlineStack, buffer, trackPositions); } if (block.FirstChild != null) { if (block.NextSibling != null) stack.Push(new BlockStackEntry(indent, block.NextSibling)); indent += 2; block = block.FirstChild; } else if (block.NextSibling != null) { block = block.NextSibling; } else if (stack.Count > 0) { var entry = stack.Pop(); indent = entry.Indent; block = entry.Target; } else { block = null; } } }
private static Inline HandleLeftSquareBracket(Subject subj, CommonMarkSettings settings) { return HandleLeftSquareBracket(subj, false, settings); }
private static Inline HandleExclamation(Subject subj, CommonMarkSettings settings) { subj.Position++; if (peek_char(subj) == '[') return HandleLeftSquareBracket(subj, true, settings); else return new Inline("!", subj.Position - 1, subj.Position); }
/// <summary> /// Adds a new block as child of another. Return the child. /// </summary> /// <remarks>Original: add_child</remarks> public static Block CreateChildBlock(Block parent, LineInfo line, BlockTag blockType, int startColumn, CommonMarkSettings settings) { // if 'parent' isn't the kind of block that can accept this child, // then back up til we hit a block that can. while (!CanContain(parent.Tag, blockType)) { Finalize(parent, line, settings); parent = parent.Parent; } var startPosition = line.IsTrackingPositions ? line.CalculateOrigin(startColumn, true) : line.LineOffset; #pragma warning disable 0618 Block child = new Block(blockType, line.LineNumber, startColumn + 1, startPosition); #pragma warning restore 0618 child.Parent = parent; child.Top = parent.Top; var lastChild = parent.LastChild; if (lastChild != null) { lastChild.NextSibling = child; #pragma warning disable 0618 child.Previous = lastChild; #pragma warning restore 0618 } else { parent.FirstChild = child; } parent.LastChild = child; return child; }
private void WriteTable(Block table, IAddChild parent, CommonMarkSettings settings, Stack <InlineStackEntry> stack) { if ((settings.AdditionalFeatures & CommonMarkAdditionalFeatures.GithubStyleTables) == 0) { throw new CommonMarkException("Table encountered in AST, but GithubStyleTables are not enabled"); } var header = table.FirstChild; var firstRow = table.FirstChild.NextSibling; Table t = new Table(); parent.AddChild(t); if (TableStyle != null) { t.Style = TableStyle; } var tableHeadRowGroup = new TableRowGroup(); t.RowGroups.Add(tableHeadRowGroup); TableRow headRow = new TableRow(); if (TableHeadStyle != null) { headRow.Style = TableHeadStyle; } tableHeadRowGroup.Rows.Add(headRow); var numHeadings = 0; var curHeaderCell = header.FirstChild; while (curHeaderCell != null) { if (numHeadings >= table.TableHeaderAlignments.Length) { break; } var alignment = table.TableHeaderAlignments[numHeadings]; numHeadings++; TableCell cell = new TableCell(); if (TableCellStyle != null) { cell.Style = TableCellStyle; } InlinesToXaml(cell, curHeaderCell.InlineContent, settings, stack); if (alignment != TableHeaderAlignment.None) { switch (alignment) { case TableHeaderAlignment.Center: cell.TextAlignment = TextAlignment.Center; break; case TableHeaderAlignment.Left: cell.TextAlignment = TextAlignment.Left; break; case TableHeaderAlignment.Right: cell.TextAlignment = TextAlignment.Right; break; default: throw new CommonMarkException("Unexpected TableHeaderAlignment [" + alignment + "]"); } } headRow.Cells.Add(cell); curHeaderCell = curHeaderCell.NextSibling; } var tableBodyRowGroup = new TableRowGroup(); t.RowGroups.Add(tableBodyRowGroup); var curRow = firstRow; while (curRow != null) { TableRow row = new TableRow(); if (TableRowStyle != null) { row.Style = TableRowStyle; } tableBodyRowGroup.Rows.Add(row); var curRowCell = curRow.FirstChild; var numCells = 0; while (curRowCell != null && numCells < numHeadings) { var alignment = table.TableHeaderAlignments[numCells]; numCells++; TableCell cell = new TableCell(); if (TableCellStyle != null) { cell.Style = TableCellStyle; } row.Cells.Add(cell); if (alignment != TableHeaderAlignment.None) { switch (alignment) { case TableHeaderAlignment.Center: cell.TextAlignment = TextAlignment.Center; break; case TableHeaderAlignment.Left: cell.TextAlignment = TextAlignment.Left; break; case TableHeaderAlignment.Right: cell.TextAlignment = TextAlignment.Right; break; default: throw new CommonMarkException("Unexpected TableHeaderAlignment [" + alignment + "]"); } } InlinesToXaml(cell, curRowCell.InlineContent, settings, stack); curRowCell = curRowCell.NextSibling; } while (numCells < numHeadings) { numCells++; TableCell cell = new TableCell(); if (TableCellStyle != null) { cell.Style = TableCellStyle; } row.Cells.Add(cell); } curRow = curRow.NextSibling; } }
/// <summary> /// Walk through the block, its children and siblings, parsing string content into inline content where appropriate. /// </summary> /// <param name="block">The document level block from which to start the processing.</param> /// <param name="refmap">The reference mapping used when parsing links.</param> /// <param name="settings">The settings that influence how the inline parsing is performed.</param> public static void ProcessInlines(Block block, Dictionary<string, Reference> refmap, CommonMarkSettings settings) { Stack<Inline> inlineStack = null; var stack = new Stack<Block>(); var subj = new Subject(refmap); StringContent sc; int delta; while (block != null) { var tag = block.Tag; var parseEmphasisInIndentedCode = tag == BlockTag.IndentedCode && 0 != (settings.AdditionalFeatures & CommonMarkAdditionalFeatures.EmphasisInIndentedCode); if (tag == BlockTag.Paragraph || tag == BlockTag.AtxHeader || tag == BlockTag.SETextHeader || tag == BlockTag.CustomContainer || parseEmphasisInIndentedCode) { sc = block.StringContent; if (sc != null) { sc.FillSubject(subj); delta = subj.Position; var inlineParameters = parseEmphasisInIndentedCode ? settings.EmphasisInlineParserParameters : settings.InlineParserParameters; block.InlineContent = InlineMethods.parse_inlines(subj, refmap, inlineParameters); block.StringContent = null; if (sc.PositionTracker != null) { sc.PositionTracker.AddBlockOffset(-delta); AdjustInlineSourcePosition(block.InlineContent, sc.PositionTracker, ref inlineStack); } } } if (block.FirstChild != null) { if (block.NextSibling != null) stack.Push(block.NextSibling); block = block.FirstChild; } else if (block.NextSibling != null) { block = block.NextSibling; } else if (stack.Count > 0) { block = stack.Pop(); } else { block = null; } } }
private static void BlocksToHtmlInner(DocumentationCommentTextWriter writer, Block block, CommonMarkSettings settings, ISymbol documentedSymbol) { var stack = new Stack <BlockStackEntry>(); var inlineStack = new Stack <InlineStackEntry>(); bool visitChildren; string stackLiteral = null; bool stackTight = false; bool tight = false; int x; while (block != null) { visitChildren = false; switch (block.Tag) { case BlockTag.Document: stackLiteral = null; stackTight = false; visitChildren = true; break; case BlockTag.Paragraph: if (tight) { InlinesToHtml(writer, block.InlineContent, settings, documentedSymbol, inlineStack); } else { writer.EnsureLine(); writer.WriteConstant("<para>"); InlinesToHtml(writer, block.InlineContent, settings, documentedSymbol, inlineStack); writer.WriteLineConstant("</para>"); } break; case BlockTag.BlockQuote: writer.EnsureLine(); writer.WriteLineConstant("<note>"); stackLiteral = "</note>"; stackTight = false; visitChildren = true; break; case BlockTag.ListItem: writer.EnsureLine(); writer.WriteConstant("<item><description>"); stackLiteral = "</description></item>"; stackTight = tight; visitChildren = true; break; case BlockTag.List: // make sure a list starts at the beginning of the line: writer.EnsureLine(); var data = block.ListData; writer.WriteConstant(data.ListType == ListType.Bullet ? "<list type=\"bullet\"" : "<list type=\"number\""); if (data.Start != 1) { writer.WriteConstant(" start=\""); writer.WriteConstant(data.Start.ToString(System.Globalization.CultureInfo.InvariantCulture)); writer.Write('\"'); } writer.WriteLineConstant(">"); stackLiteral = "</list>"; stackTight = data.IsTight; visitChildren = true; break; case BlockTag.AtxHeading: case BlockTag.SetextHeading: writer.EnsureLine(); x = block.Heading.Level; writer.WriteConstant(x > 0 && x < 7 ? HeaderOpenerTags[x - 1] : "<h" + x.ToString(CultureInfo.InvariantCulture) + ">"); InlinesToHtml(writer, block.InlineContent, settings, documentedSymbol, inlineStack); writer.WriteLineConstant(x > 0 && x < 7 ? HeaderCloserTags[x - 1] : "</h" + x.ToString(CultureInfo.InvariantCulture) + ">"); break; case BlockTag.IndentedCode: writer.EnsureLine(); writer.WriteConstant("<code>"); EscapeHtml(block.StringContent, writer); writer.WriteLineConstant("</code>"); break; case BlockTag.FencedCode: writer.EnsureLine(); writer.WriteConstant("<code"); var info = block.FencedCodeData.Info; if (info != null && info.Length > 0) { x = info.IndexOf(' '); if (x == -1) { x = info.Length; } writer.WriteConstant(" language=\""); EscapeHtml(info.Substring(0, x), writer); writer.Write('\"'); } writer.Write('>'); writer.WriteLine(); EscapeHtml(block.StringContent, writer); writer.WriteLineConstant("</code>"); break; case BlockTag.HtmlBlock: writer.Write(block.StringContent.ToString(new StringBuilder())); break; case BlockTag.ThematicBreak: writer.WriteLineConstant("<hr />"); break; case BlockTag.ReferenceDefinition: break; default: throw new CommonMarkException("Block type " + block.Tag + " is not supported.", block); } if (visitChildren) { stack.Push(new BlockStackEntry(stackLiteral, block.NextSibling, tight)); tight = stackTight; block = block.FirstChild; } else if (block.NextSibling != null) { block = block.NextSibling; } else { block = null; } while (block == null && stack.Count > 0) { var entry = stack.Pop(); writer.WriteLineConstant(entry.Literal); tight = entry.IsTight; block = entry.Target; } } }
// Parse a link or the link portion of an image, or return a fallback. static Reference ParseLinkDetails(Subject subj, CommonMarkSettings settings) { int n; int sps; int endlabel, starturl, endurl, starttitle, endtitle, endall; string url, title; endlabel = subj.Position; var c = peek_char(subj); if (c == '(' && ((sps = Scanner.scan_spacechars(subj.Buffer, subj.Position + 1, subj.Length)) > -1) && ((n = Scanner.scan_link_url(subj.Buffer, subj.Position + 1 + sps, subj.Length)) > -1)) { // try to parse an explicit link: starturl = subj.Position + 1 + sps; // after ( endurl = starturl + n; starttitle = endurl + Scanner.scan_spacechars(subj.Buffer, endurl, subj.Length); // ensure there are spaces btw url and title endtitle = (starttitle == endurl) ? starttitle : starttitle + Scanner.scan_link_title(subj.Buffer, starttitle, subj.Length); endall = endtitle + Scanner.scan_spacechars(subj.Buffer, endtitle, subj.Length); if (endall < subj.Length && subj.Buffer[endall] == ')') { subj.Position = endall + 1; url = subj.Buffer.Substring(starturl, endurl - starturl); url = CleanUrl(url); title = subj.Buffer.Substring(starttitle, endtitle - starttitle); title = CleanTitle(title); return new Reference() { Title = title, Url = url }; } } else if (c == '[' || c == ' ' || c == '\n') { var label = ParseReferenceLabel(subj); if (label != null) { if (label.Value.Length == 0) return Reference.SelfReference; var details = LookupReference(subj.ReferenceMap, label.Value, settings); if (details != null) return details; // rollback the subject but return InvalidReference so that the caller knows not to // parse 'foo' from [foo][bar]. subj.Position = endlabel; return Reference.InvalidReference; } } // rollback the subject position because didn't match anything. subj.Position = endlabel; return null; }
/// <summary> /// Writes the inline list to the given writer as HTML code. /// </summary> private static void InlinesToHtml(DocumentationCommentTextWriter writer, Inline inline, CommonMarkSettings settings, ISymbol documentedSymbol, Stack <InlineStackEntry> stack) { var uriResolver = settings.UriResolver; bool withinLink = false; bool stackWithinLink = false; bool visitChildren; string stackLiteral = null; while (inline != null) { visitChildren = false; switch (inline.Tag) { case InlineTag.String: EscapeHtml(inline.LiteralContent, writer); break; case InlineTag.LineBreak: writer.WriteLineConstant("<br />"); break; case InlineTag.SoftBreak: if (settings.RenderSoftLineBreaksAsLineBreaks) { writer.WriteLineConstant("<br />"); } else { writer.WriteLine(); } break; case InlineTag.Code: if (documentedSymbol.HasAnyParameter(inline.LiteralContent, StringComparer.Ordinal)) { writer.WriteConstant("<paramref name=\""); EscapeHtml(inline.LiteralContent, writer); writer.WriteConstant("\"/>"); } else if (documentedSymbol.HasAnyTypeParameter(inline.LiteralContent, StringComparer.Ordinal)) { writer.WriteConstant("<typeparamref name=\""); EscapeHtml(inline.LiteralContent, writer); writer.WriteConstant("\"/>"); } else { writer.WriteConstant("<c>"); EscapeHtml(inline.LiteralContent, writer); writer.WriteConstant("</c>"); } break; case InlineTag.RawHtml: writer.Write(inline.LiteralContent); break; case InlineTag.Link: if (withinLink) { writer.Write('['); stackLiteral = "]"; stackWithinLink = withinLink; visitChildren = true; } else { writer.WriteConstant("<see href=\""); if (uriResolver != null) { EscapeUrl(uriResolver(inline.TargetUrl), writer); } else { EscapeUrl(inline.TargetUrl, writer); } writer.Write('\"'); if (inline.LiteralContent.Length > 0) { writer.WriteConstant(" title=\""); EscapeHtml(inline.LiteralContent, writer); writer.Write('\"'); } writer.Write('>'); visitChildren = true; stackWithinLink = true; stackLiteral = "</see>"; } break; case InlineTag.Image: writer.WriteConstant("<img src=\""); if (uriResolver != null) { EscapeUrl(uriResolver(inline.TargetUrl), writer); } else { EscapeUrl(inline.TargetUrl, writer); } writer.WriteConstant("\" alt=\""); InlinesToPlainText(writer, inline.FirstChild, stack); writer.Write('\"'); if (inline.LiteralContent.Length > 0) { writer.WriteConstant(" title=\""); EscapeHtml(inline.LiteralContent, writer); writer.Write('\"'); } writer.WriteConstant(" />"); break; case InlineTag.Strong: writer.WriteConstant("<strong>"); stackLiteral = "</strong>"; stackWithinLink = withinLink; visitChildren = true; break; case InlineTag.Emphasis: writer.WriteConstant("<em>"); stackLiteral = "</em>"; visitChildren = true; stackWithinLink = withinLink; break; case InlineTag.Strikethrough: writer.WriteConstant("<del>"); stackLiteral = "</del>"; visitChildren = true; stackWithinLink = withinLink; break; default: throw new CommonMarkException("Inline type " + inline.Tag + " is not supported.", inline); } if (visitChildren) { stack.Push(new InlineStackEntry(stackLiteral, inline.NextSibling, withinLink)); withinLink = stackWithinLink; inline = inline.FirstChild; } else if (inline.NextSibling != null) { inline = inline.NextSibling; } else { inline = null; } while (inline == null && stack.Count > 0) { var entry = stack.Pop(); writer.WriteConstant(entry.Literal); inline = entry.Target; withinLink = entry.IsWithinLink; } } }
/// <summary> /// Walk through the block, its children and siblings, parsing string content into inline content where appropriate. /// </summary> /// <param name="block">The document level block from which to start the processing.</param> /// <param name="refmap">The reference mapping used when parsing links.</param> /// <param name="settings">The settings that influence how the inline parsing is performed.</param> public static void ProcessInlines(Block block, Dictionary<string, Reference> refmap, CommonMarkSettings settings) { Stack<Inline> inlineStack = null; var stack = new Stack<Block>(); var parsers = settings.InlineParsers; var specialCharacters = settings.InlineParserSpecialCharacters; var subj = new Subject(refmap); StringContent sc; int delta; while (block != null) { var tag = block.Tag; if (tag == BlockTag.Paragraph || tag == BlockTag.AtxHeader || tag == BlockTag.SETextHeader) { sc = block.StringContent; if (sc != null) { sc.FillSubject(subj); delta = subj.Position; block.InlineContent = InlineMethods.parse_inlines(subj, refmap, parsers, specialCharacters); block.StringContent = null; if (sc.PositionTracker != null) { sc.PositionTracker.AddBlockOffset(-delta); AdjustInlineSourcePosition(block.InlineContent, sc.PositionTracker, ref inlineStack); } } } if (block.FirstChild != null) { if (block.NextSibling != null) stack.Push(block.NextSibling); block = block.FirstChild; } else if (block.NextSibling != null) { block = block.NextSibling; } else if (stack.Count > 0) { block = stack.Pop(); } else { block = null; } } }
private static void BlocksToHtmlInner(HtmlTextWriter writer, Block block, CommonMarkSettings settings) { var stack = new Stack<BlockStackEntry>(); var inlineStack = new Stack<InlineStackEntry>(); bool visitChildren; string stackLiteral = null; bool stackTight = false; bool tight = false; bool trackPositions = settings.TrackSourcePosition; int x; while (block != null) { visitChildren = false; switch (block.Tag) { case BlockTag.Document: stackLiteral = null; stackTight = false; visitChildren = true; break; case BlockTag.Paragraph: if (tight) { InlinesToHtml(writer, block.InlineContent, settings, inlineStack); } else { writer.EnsureLine(); writer.WriteConstant("<p"); if (trackPositions) PrintPosition(writer, block); writer.Write('>'); InlinesToHtml(writer, block.InlineContent, settings, inlineStack); writer.WriteLineConstant("</p>"); } break; case BlockTag.BlockQuote: writer.EnsureLine(); writer.WriteConstant("<blockquote"); if (trackPositions) PrintPosition(writer, block); writer.WriteLine('>'); stackLiteral = "</blockquote>"; stackTight = false; visitChildren = true; break; case BlockTag.ListItem: writer.EnsureLine(); writer.WriteConstant("<li"); if (trackPositions) PrintPosition(writer, block); writer.Write('>'); stackLiteral = "</li>"; stackTight = tight; visitChildren = true; break; case BlockTag.List: // make sure a list starts at the beginning of the line: writer.EnsureLine(); var data = block.ListData; writer.WriteConstant(data.ListType == ListType.Bullet ? "<ul" : "<ol"); if (data.Start != 1) { writer.WriteConstant(" start=\""); writer.WriteConstant(data.Start.ToString(CultureInfo.InvariantCulture)); writer.Write('\"'); } if (trackPositions) PrintPosition(writer, block); writer.WriteLine('>'); stackLiteral = data.ListType == ListType.Bullet ? "</ul>" : "</ol>"; stackTight = data.IsTight; visitChildren = true; break; case BlockTag.AtxHeader: case BlockTag.SETextHeader: writer.EnsureLine(); x = block.HeaderLevel; if (trackPositions) { writer.WriteConstant("<h" + x.ToString(CultureInfo.InvariantCulture)); PrintPosition(writer, block); writer.Write('>'); InlinesToHtml(writer, block.InlineContent, settings, inlineStack); writer.WriteLineConstant(x > 0 && x < 7 ? HeaderCloserTags[x - 1] : "</h" + x.ToString(CultureInfo.InvariantCulture) + ">"); } else { writer.WriteConstant(x > 0 && x < 7 ? HeaderOpenerTags[x - 1] : "<h" + x.ToString(CultureInfo.InvariantCulture) + ">"); InlinesToHtml(writer, block.InlineContent, settings, inlineStack); writer.WriteLineConstant(x > 0 && x < 7 ? HeaderCloserTags[x - 1] : "</h" + x.ToString(CultureInfo.InvariantCulture) + ">"); } break; case BlockTag.IndentedCode: case BlockTag.FencedCode: writer.EnsureLine(); writer.WriteConstant("<pre><code"); if (trackPositions) PrintPosition(writer, block); var info = block.FencedCodeData == null ? null : block.FencedCodeData.Info; if (info != null && info.Length > 0) { x = info.IndexOf(' '); if (x == -1) x = info.Length; writer.WriteConstant(" class=\"language-"); EscapeHtml(new StringPart(info, 0, x), writer); writer.Write('\"'); } writer.Write('>'); EscapeHtml(block.StringContent, writer); writer.WriteLineConstant("</code></pre>"); break; case BlockTag.HtmlBlock: // cannot output source position for HTML blocks block.StringContent.WriteTo(writer); break; case BlockTag.HorizontalRuler: if (trackPositions) { writer.WriteConstant("<hr"); PrintPosition(writer, block); writer.WriteLine(); } else { writer.WriteLineConstant("<hr />"); } break; case BlockTag.ReferenceDefinition: break; default: throw new CommonMarkException("Block type " + block.Tag + " is not supported.", block); } if (visitChildren) { stack.Push(new BlockStackEntry(stackLiteral, block.NextSibling, tight)); tight = stackTight; block = block.FirstChild; } else if (block.NextSibling != null) { block = block.NextSibling; } else { block = null; } while (block == null && stack.Count > 0) { var entry = stack.Pop(); writer.WriteLineConstant(entry.Literal); tight = entry.IsTight; block = entry.Target; } } }
public TableFormatter(TextWriter target, CommonMarkSettings settings) : base(target, settings) { }
/// <summary> /// Write the block data to the given writer. /// </summary> public static void PrintBlocks(TextWriter writer, Block block, CommonMarkSettings settings) { int indent = 0; var stack = new Stack <BlockStackEntry>(); var inlineStack = new Stack <InlineStackEntry>(); var buffer = new StringBuilder(); var trackPositions = settings.TrackSourcePosition; while (block != null) { writer.Write(new string(' ', indent)); switch (block.Tag) { case BlockTag.Document: writer.Write("document"); PrintPosition(trackPositions, writer, block); break; case BlockTag.BlockQuote: writer.Write("block_quote"); PrintPosition(trackPositions, writer, block); break; case BlockTag.ListItem: writer.Write("list_item"); PrintPosition(trackPositions, writer, block); break; case BlockTag.List: writer.Write("list"); PrintPosition(trackPositions, writer, block); var data = block.ListData; if (data.ListType == ListType.Ordered) { writer.Write(" (type=ordered tight={0} start={1} delim={2})", data.IsTight, data.Start, data.Delimiter); } else { writer.Write("(type=bullet tight={0} bullet_char={1})", data.IsTight, data.BulletChar); } break; case BlockTag.AtxHeader: writer.Write("atx_header"); PrintPosition(trackPositions, writer, block); writer.Write(" (level={0})", block.HeaderLevel); break; case BlockTag.SETextHeader: writer.Write("setext_header"); PrintPosition(trackPositions, writer, block); writer.Write(" (level={0})", block.HeaderLevel); break; case BlockTag.Paragraph: writer.Write("paragraph"); PrintPosition(trackPositions, writer, block); break; case BlockTag.HorizontalRuler: writer.Write("hrule"); PrintPosition(trackPositions, writer, block); break; case BlockTag.IndentedCode: writer.Write("indented_code {0}", format_str(block.StringContent.ToString(buffer), buffer)); PrintPosition(trackPositions, writer, block); writer.Write(' '); writer.Write(format_str(block.StringContent.ToString(buffer), buffer)); break; case BlockTag.FencedCode: writer.Write("fenced_code"); PrintPosition(trackPositions, writer, block); writer.Write(" length={0} info={1} {2}", block.FencedCodeData.FenceLength, format_str(block.FencedCodeData.Info, buffer), format_str(block.StringContent.ToString(buffer), buffer)); break; case BlockTag.HtmlBlock: writer.Write("html_block"); PrintPosition(trackPositions, writer, block); writer.Write(' '); writer.Write(format_str(block.StringContent.ToString(buffer), buffer)); break; case BlockTag.ReferenceDefinition: writer.Write("reference_def"); PrintPosition(trackPositions, writer, block); break; default: throw new CommonMarkException("Block type " + block.Tag + " is not supported.", block); } writer.WriteLine(); if (block.InlineContent != null) { PrintInlines(writer, block.InlineContent, indent + 2, inlineStack, buffer, trackPositions); } if (block.FirstChild != null) { if (block.NextSibling != null) { stack.Push(new BlockStackEntry(indent, block.NextSibling)); } indent += 2; block = block.FirstChild; } else if (block.NextSibling != null) { block = block.NextSibling; } else if (stack.Count > 0) { var entry = stack.Pop(); indent = entry.Indent; block = entry.Target; } else { block = null; } } }
// Process one line at a time, modifying a block. // Returns 0 if successful. curptr is changed to point to // the currently open block. public static void IncorporateLine(LineInfo line, ref Block curptr, CommonMarkSettings settings) { var parameters = settings.BlockParserParameters; var ln = line.Line; Block last_matched_container; // offset is the char position in the line var offset = 0; // column is the virtual position in the line that takes TAB expansion into account var column = 0; // the char position of the first non-space char int first_nonspace; // the virtual position of the first non-space chart, that includes TAB expansion int first_nonspace_column; int matched; int i; ListData data; bool all_matched = true; Block cur = curptr; var blank = false; char curChar; int indent; // container starts at the document root. var container = cur.Top; // for each containing block, try to parse the associated line start. // bail out on failure: container will point to the last matching block. while (container.LastChild != null && container.LastChild.IsOpen) { container = container.LastChild; FindFirstNonspace(ln, offset, column, out first_nonspace, out first_nonspace_column, out curChar); indent = first_nonspace_column - column; blank = curChar == '\n'; switch (container.Tag) { case BlockTag.BlockQuote: { if (indent <= 3 && curChar == '>') { AdvanceOffset(ln, indent + 1, true, ref offset, ref column); if (ln[offset] == ' ') offset++; } else { all_matched = false; } break; } case BlockTag.ListItem: { if (indent >= container.ListData.MarkerOffset + container.ListData.Padding) { AdvanceOffset(ln, container.ListData.MarkerOffset + container.ListData.Padding, true, ref offset, ref column); } else if (blank && container.FirstChild != null) { // if container->first_child is NULL, then the opening line // of the list item was blank after the list marker; in this // case, we are done with the list item. AdvanceOffset(ln, first_nonspace - offset, false, ref offset, ref column); } else { all_matched = false; } break; } case BlockTag.IndentedCode: { if (indent >= CODE_INDENT) AdvanceOffset(ln, CODE_INDENT, true, ref offset, ref column); else if (blank) AdvanceOffset(ln, first_nonspace - offset, false, ref offset, ref column); else all_matched = false; break; } case BlockTag.AtxHeader: case BlockTag.SETextHeader: { // a header can never contain more than one line all_matched = false; if (blank) container.IsLastLineBlank = true; break; } case BlockTag.FencedCode: case BlockTag.CustomContainer: { // -1 means we've seen closer if (container.FencedCodeData.FenceLength == -1) { all_matched = false; if (blank) container.IsLastLineBlank = true; } else { // skip optional spaces of fence offset i = container.FencedCodeData.FenceOffset; while (i > 0 && ln[offset] == ' ') { offset++; column++; i--; } } break; } case BlockTag.HtmlBlock: { // all other block types can accept blanks if (blank && container.HtmlBlockType >= HtmlBlockType.InterruptingBlock) { container.IsLastLineBlank = true; all_matched = false; } break; } case BlockTag.Paragraph: { if (blank) { container.IsLastLineBlank = true; all_matched = false; } break; } } if (!all_matched) { container = container.Parent; // back up to last matching block break; } } last_matched_container = container; // check to see if we've hit 2nd blank line, break out of list: if (blank && container.IsLastLineBlank) BreakOutOfLists(ref container, line, settings); var maybeLazy = cur.Tag == BlockTag.Paragraph; // unless last matched container is code block, try new container starts: while (container.Tag != BlockTag.FencedCode && container.Tag != BlockTag.IndentedCode && container.Tag != BlockTag.CustomContainer && container.Tag != BlockTag.HtmlBlock) { FindFirstNonspace(ln, offset, column, out first_nonspace, out first_nonspace_column, out curChar); indent = first_nonspace_column - column; blank = curChar == '\n'; var indented = indent >= CODE_INDENT; if (!indented && curChar == '>') { AdvanceOffset(ln, first_nonspace + 1 - offset, false, ref offset, ref column); // optional following character if (ln[offset] == ' ') { offset++; column++; } container = CreateChildBlock(container, line, BlockTag.BlockQuote, first_nonspace, settings); } else if (!indented && curChar == '#' && 0 != (matched = Scanner.scan_atx_header_start(ln, first_nonspace, ln.Length, out i))) { AdvanceOffset(ln, first_nonspace + matched - offset, false, ref offset, ref column); container = CreateChildBlock(container, line, BlockTag.AtxHeader, first_nonspace, settings); container.HeaderLevel = i; } else if (!indented && parameters.IsFenceDelimiter(curChar) && 0 != (matched = Scanner.scan_open_code_fence(ln, first_nonspace, ln.Length, parameters))) { var blockTag = curChar == ':' ? BlockTag.CustomContainer : BlockTag.FencedCode; container = CreateChildBlock(container, line, blockTag, first_nonspace, settings); container.FencedCodeData = new FencedCodeData(); container.FencedCodeData.FenceChar = curChar; container.FencedCodeData.FenceLength = matched; container.FencedCodeData.FenceOffset = first_nonspace - offset; AdvanceOffset(ln, first_nonspace + matched - offset, false, ref offset, ref column); } else if (!indented && curChar == '<' && (0 != (matched = (int)Scanner.scan_html_block_start(ln, first_nonspace, ln.Length)) || (container.Tag != BlockTag.Paragraph && 0 != (matched = (int)Scanner.scan_html_block_start_7(ln, first_nonspace, ln.Length))) )) { container = CreateChildBlock(container, line, BlockTag.HtmlBlock, first_nonspace, settings); container.HtmlBlockType = (HtmlBlockType)matched; // note, we don't adjust offset because the tag is part of the text } else if (!indented && container.Tag == BlockTag.Paragraph && parameters.IsSETextHeaderDelimiter(curChar) && 0 != (matched = Scanner.scan_setext_header_line(ln, first_nonspace, ln.Length, parameters)) && ContainsSingleLine(container.StringContent)) { container.Tag = BlockTag.SETextHeader; container.HeaderLevel = matched; AdvanceOffset(ln, ln.Length - 1 - offset, false, ref offset, ref column); } else if (!indented && !(container.Tag == BlockTag.Paragraph && !all_matched) && 0 != (Scanner.scan_hrule(ln, first_nonspace, ln.Length))) { // it's only now that we know the line is not part of a setext header: container = CreateChildBlock(container, line, BlockTag.HorizontalRuler, first_nonspace, settings); Finalize(container, line, settings); container = container.Parent; AdvanceOffset(ln, ln.Length - 1 - offset, false, ref offset, ref column); } else if ((!indented || container.Tag == BlockTag.List) && 0 != (matched = ParseListMarker(ln, first_nonspace, out data))) { // compute padding: AdvanceOffset(ln, first_nonspace + matched - offset, false, ref offset, ref column); i = 0; while (i <= 5 && ln[offset + i] == ' ') i++; // i = number of spaces after marker, up to 5 if (i >= 5 || i < 1 || ln[offset] == '\n') { data.Padding = matched + 1; if (i > 0) { column++; offset++; } } else { data.Padding = matched + i; AdvanceOffset(ln, i, true, ref offset, ref column); } // check container; if it's a list, see if this list item // can continue the list; otherwise, create a list container. data.MarkerOffset = indent; if (container.Tag != BlockTag.List || !ListsMatch(container.ListData, data)) { container = CreateChildBlock(container, line, BlockTag.List, first_nonspace, settings); container.ListData = data; } // add the list item container = CreateChildBlock(container, line, BlockTag.ListItem, first_nonspace, settings); container.ListData = data; } else if (indented && !maybeLazy && !blank) { AdvanceOffset(ln, CODE_INDENT, true, ref offset, ref column); container = CreateChildBlock(container, line, BlockTag.IndentedCode, offset, settings); } else { break; } if (AcceptsLines(container.Tag)) { // if it's a line container, it can't contain other containers break; } maybeLazy = false; } // what remains at offset is a text line. add the text to the // appropriate container. FindFirstNonspace(ln, offset, column, out first_nonspace, out first_nonspace_column, out curChar); indent = first_nonspace_column - column; blank = curChar == '\n'; if (blank && container.LastChild != null) { container.LastChild.IsLastLineBlank = true; } // block quote lines are never blank as they start with > // and we don't count blanks in fenced code for purposes of tight/loose // lists or breaking out of lists. we also don't set last_line_blank // on an empty list item. container.IsLastLineBlank = (blank && container.Tag != BlockTag.BlockQuote && container.Tag != BlockTag.SETextHeader && container.Tag != BlockTag.FencedCode && container.Tag != BlockTag.CustomContainer && !(container.Tag == BlockTag.ListItem && container.FirstChild == null && container.SourcePosition >= line.LineOffset)); Block cont = container; while (cont.Parent != null) { cont.Parent.IsLastLineBlank = false; cont = cont.Parent; } if (cur != last_matched_container && container == last_matched_container && !blank && cur.Tag == BlockTag.Paragraph && cur.StringContent.Length > 0) { AddLine(cur, line, ln, offset); } else { // not a lazy continuation // finalize any blocks that were not matched and set cur to container: while (cur != last_matched_container) { Finalize(cur, line, settings); cur = cur.Parent; if (cur == null) throw new CommonMarkException("Cannot finalize container block. Last matched container tag = " + last_matched_container.Tag); } if (container.Tag == BlockTag.IndentedCode) { AddLine(container, line, ln, offset); } else if (container.Tag == BlockTag.FencedCode || container.Tag == BlockTag.CustomContainer) { if ((indent <= 3 && curChar == container.FencedCodeData.FenceChar) && (0 != Scanner.scan_close_code_fence(ln, first_nonspace, container.FencedCodeData.FenceLength, ln.Length, parameters))) { // if closing fence, set fence length to -1. it will be closed when the next line is processed. container.FencedCodeData.FenceLength = -1; } else { AddLine(container, line, ln, offset); } } else if (container.Tag == BlockTag.HtmlBlock) { AddLine(container, line, ln, offset); if (Scanner.scan_html_block_end(container.HtmlBlockType, ln, first_nonspace, ln.Length)) { Finalize(container, line, settings); container = container.Parent; } } else if (blank) { // ??? do nothing } else if (container.Tag == BlockTag.AtxHeader) { int p = ln.Length - 1; // trim trailing spaces while (p >= 0 && (ln[p] == ' ' || ln[p] == '\n')) p--; // if string ends in #s, remove these: while (p >= 0 && ln[p] == '#') p--; // there must be a space before the last hashtag if (p < 0 || ln[p] != ' ') p = ln.Length - 1; AddLine(container, line, ln, first_nonspace, p - first_nonspace + 1); Finalize(container, line, settings); container = container.Parent; } else if (AcceptsLines(container.Tag)) { AddLine(container, line, ln, first_nonspace); } else if (container.Tag != BlockTag.HorizontalRuler && container.Tag != BlockTag.SETextHeader) { // create paragraph container for line container = CreateChildBlock(container, line, BlockTag.Paragraph, first_nonspace, settings); AddLine(container, line, ln, first_nonspace); } else { Utilities.Warning("Line {0} with container type {1} did not match any condition:\n\"{2}\"", line.LineNumber, container.Tag, ln); } curptr = container; } }
public static void BlocksToHtml(TextWriter writer, Block block, CommonMarkSettings settings) { var wrapper = new HtmlTextWriter(writer); BlocksToHtmlInner(wrapper, block, settings); }
private void BlocksToXamlInner(FlowDocument parent, Block block, CommonMarkSettings settings) { _checkBoxNumber = 0; var stack = new Stack <BlockStackEntry>(); var inlineStack = new Stack <InlineStackEntry>(); bool stackTight = false; bool tight = false; bool trackPositions = settings.TrackSourcePosition; int x; IAddChild blockParent = parent; while (block != null) { var visitChildren = false; IAddChild lastParent = null; switch (block.Tag) { case BlockTag.Document: stackTight = false; visitChildren = true; lastParent = parent; break; case BlockTag.Paragraph: if (tight) { InlinesToXaml(blockParent, block.InlineContent, settings, inlineStack); } else { Paragraph paragraph = new Paragraph(); if (trackPositions) { PrintPosition(paragraph, block); } InlinesToXaml(paragraph, block.InlineContent, settings, inlineStack); blockParent.AddChild(paragraph); } break; case BlockTag.BlockQuote: Section blockquoteParagraph = new Section(); if (trackPositions) { PrintPosition(blockquoteParagraph, block); } if (QuoteStyle != null) { blockquoteParagraph.Style = QuoteStyle; } blockParent.AddChild(blockquoteParagraph); lastParent = blockParent; blockParent = blockquoteParagraph; stackTight = true; visitChildren = true; break; case BlockTag.ListItem: stackTight = tight; if (block.ListData != null && block.ListData.ListType == ListType.TaskList) { BlockUIContainer ctnr = (BlockUIContainer)blockParent; StackPanel sp = (StackPanel)ctnr.Child; visitChildren = false; TextBlock spcb = new TextBlock(); spcb.Style = TodoTextStyle; Inline checkBoxInline = block.FirstChild?.InlineContent; if (checkBoxInline != null) { InlinesToXaml(spcb, checkBoxInline, settings, inlineStack); } CheckBox bt = new CheckBox { IsChecked = block.TaskListItemIsChecked, Content = spcb, Tag = _checkBoxNumber }; bt.CommandParameter = bt; bt.Command = CheckBoxCheckedCommand; bt.Style = TodoCheckBoxStyle; sp.Children.Add(bt); //TODO: Add child stack panel and add block.FirstChild.NextSibling _checkBoxNumber++; } else { ListItem listItem = new ListItem(); if (ListItemStyle != null) { listItem.Style = ListItemStyle; } if (trackPositions) { PrintPosition(listItem, block); } blockParent.AddChild(listItem); lastParent = blockParent; blockParent = listItem; visitChildren = true; } break; case BlockTag.List: var data = block.ListData; IAddChild list; if (data.ListType == ListType.TaskList) { StackPanel sp = new StackPanel(); list = new BlockUIContainer(sp); } else { List theList = new List(); list = theList; theList.MarkerStyle = data.ListType == ListType.Bullet ? TextMarkerStyle.Disc : TextMarkerStyle.Decimal; if (ListStyle != null) { theList.Style = ListStyle; } } if (trackPositions) { PrintPosition((FrameworkContentElement)list, block); } // TODO: Check if first child starts with [ ] then it is a todo-list item // TODO: Set list.StartIndex if > 1 blockParent.AddChild(list); lastParent = blockParent; blockParent = list; stackTight = data.IsTight; visitChildren = true; break; case BlockTag.AtxHeading: case BlockTag.SetextHeading: Paragraph headerParagraph = new Paragraph(); if (trackPositions) { PrintPosition(headerParagraph, block); } InlinesToXaml(headerParagraph, block.InlineContent, settings, inlineStack); x = block.Heading.Level; switch (x) { case 1: headerParagraph.Style = Heading1Style; break; case 2: headerParagraph.Style = Heading2Style; break; case 3: headerParagraph.Style = Heading3Style; break; case 4: headerParagraph.Style = Heading4Style; break; default: headerParagraph.Style = Heading1Style; break; } blockParent.AddChild(headerParagraph); break; case BlockTag.IndentedCode: case BlockTag.FencedCode: Paragraph codeblockParagraph = new Paragraph(); if (trackPositions) { PrintPosition(codeblockParagraph, block); } if (CodeBlockStyle != null) { codeblockParagraph.Style = CodeBlockStyle; } var info = block.FencedCodeData == null ? null : block.FencedCodeData.Info; if (info != null && info.Length > 0) { //x = info.IndexOf(' '); //if (x == -1) // x = info.Length; //parent.WriteConstant(" class=\"language-"); //EscapeHtml(new StringPart(info, 0, x), parent); //parent.Write('\"'); } EscapeHtml(block.StringContent, codeblockParagraph); blockParent.AddChild(codeblockParagraph); break; case BlockTag.HtmlBlock: // cannot output source position for HTML blocks // block.StringContent.WriteTo(parent); //TODO: Unable to convert html to break; case BlockTag.ThematicBreak: var line = new Line() { X2 = 1, StrokeThickness = 1.0 }; var container = new BlockUIContainer(line); if (trackPositions) { PrintPosition(container, block); } if (LineStyle != null) { line.Style = LineStyle; } blockParent.AddChild(container); break; case BlockTag.Table: WriteTable(block, blockParent, settings, inlineStack); break; case BlockTag.ReferenceDefinition: break; default: throw new CommonMarkException("Block type " + block.Tag + " is not supported.", block); } if (visitChildren) { stack.Push(new BlockStackEntry(lastParent, block.NextSibling, tight)); tight = stackTight; block = block.FirstChild; } else if (block.NextSibling != null) { block = block.NextSibling; } else { block = null; } while (block == null && stack.Count > 0) { var entry = stack.Pop(); blockParent = entry.Parent; tight = entry.IsTight; block = entry.Target; } } }
/// <summary> /// Collapses internal whitespace to single space, removes leading/trailing whitespace, folds case. /// </summary> private static string NormalizeReference(StringPart s, CommonMarkSettings settings) { if (s.Length == 0) return string.Empty; var result = NormalizeWhitespace(s.Source, s.StartIndex, s.Length); if (0 == (settings.AdditionalFeatures & CommonMarkAdditionalFeatures.RespectReferenceCase)) result = result.ToUpperInvariant(); return result; }
/// <summary> /// Checks if the reference dictionary contains a reference with the given label and returns it, /// otherwise returns <c>null</c>. /// Returns <see cref="Reference.InvalidReference"/> if the reference label is not valid. /// </summary> private static Reference LookupReference(Dictionary<string, Reference> refmap, StringPart lab, CommonMarkSettings settings) { if (refmap == null) return null; if (lab.Length > Reference.MaximumReferenceLabelLength) return Reference.InvalidReference; string label = NormalizeReference(lab, settings); Reference r; if (refmap.TryGetValue(label, out r)) return r; return null; }
public MarkdownHighlightingColorizer() { _commonMarkSettings = CommonMarkSettings.Default.Clone(); _commonMarkSettings.TrackSourcePosition = true; }
// Parse reference. Assumes string begins with '[' character. // Modify refmap if a reference is encountered. // Return 0 if no reference found, otherwise position of subject // after reference is parsed. public static int ParseReference(Subject subj, CommonMarkSettings settings) { string title; var startPos = subj.Position; // parse label: var lab = ParseReferenceLabel(subj); if (lab == null || lab.Value.Length > Reference.MaximumReferenceLabelLength) goto INVALID; if (!Scanner.HasNonWhitespace(lab.Value)) goto INVALID; // colon: if (peek_char(subj) == ':') subj.Position++; else goto INVALID; // parse link url: spnl(subj); var matchlen = Scanner.scan_link_url(subj.Buffer, subj.Position, subj.Length); if (matchlen == 0) goto INVALID; var url = subj.Buffer.Substring(subj.Position, matchlen); url = CleanUrl(url); subj.Position += matchlen; // parse optional link_title var beforetitle = subj.Position; spnl(subj); matchlen = Scanner.scan_link_title(subj.Buffer, subj.Position, subj.Length); if (matchlen > 0) { title = subj.Buffer.Substring(subj.Position, matchlen); title = CleanTitle(title); subj.Position += matchlen; } else { subj.Position = beforetitle; title = string.Empty; } char c; // parse final spaces and newline: while ((c = peek_char(subj)) == ' ') subj.Position++; if (c == '\n') { subj.Position++; } else if (c != '\0') { if (matchlen > 0) { // try rewinding before title subj.Position = beforetitle; while ((c = peek_char(subj)) == ' ') subj.Position++; if (c == '\n') subj.Position++; else if (c != '\0') goto INVALID; } else { goto INVALID; } } // insert reference into refmap AddReference(subj.ReferenceMap, lab.Value, url, title, settings); return subj.Position; INVALID: subj.Position = startPos; return 0; }