public override bool Match(InlineProcessor processor, ref StringSlice slice) { if (!ExtensionsHelper.MatchStart(ref slice, StartString, true)) { return(false); } var text = ExtensionsHelper.TryGetStringBeforeChars(new char[] { '\"', '\n' }, ref slice); if (text == null || text.IndexOf('\n') != -1) { return(false); } if (!ExtensionsHelper.MatchStart(ref slice, EndString, true)) { return(false); } processor.Inline = new NolocInline() { Text = text }; return(true); }
public override BlockState TryContinue(BlockProcessor processor, Block block) { if (processor.IsBlankLine) { return(BlockState.Continue); } var slice = processor.Line; var monikerRange = (MonikerRangeBlock)block; ExtensionsHelper.SkipSpaces(ref slice); if (!ExtensionsHelper.MatchStart(ref slice, new string(':', monikerRange.ColonCount))) { return(BlockState.Continue); } ExtensionsHelper.SkipSpaces(ref slice); if (!ExtensionsHelper.MatchStart(ref slice, "moniker-end", false)) { return(BlockState.Continue); } var c = ExtensionsHelper.SkipSpaces(ref slice); if (!c.IsZero()) { Logger.LogWarning($"MonikerRange have some invalid chars in the ending."); } block.UpdateSpanEnd(slice.End); return(BlockState.BreakDiscard); }
public override bool Match(InlineProcessor processor, ref StringSlice slice) { if (!ExtensionsHelper.MatchStart(ref slice, StartString, false)) { return(false); } else { if (slice.CurrentChar == '-') { slice.NextChar(); } } var includeFile = new InclusionInline(); var context = new InclusionContext(); if (!ExtensionsHelper.MatchLink(ref slice, ref context)) { return(false); } includeFile.Context = context; processor.Inline = includeFile; return(true); }
public override bool Match(InlineProcessor processor, ref StringSlice slice) { if (!ExtensionsHelper.MatchStart(ref slice, StartString, false)) { return(false); } else { if (slice.CurrentChar == '-') { slice.NextChar(); } } var includeFile = new InclusionInline(); string title = null, path = null; if (!ExtensionsHelper.MatchLink(ref slice, ref title, ref path)) { return(false); } includeFile.Title = title; includeFile.IncludedFilePath = path; processor.Inline = includeFile; return(true); }
public override bool Match(InlineProcessor processor, ref StringSlice slice) { var startPosition = processor.GetSourcePosition(slice.Start, out var line, out var column); if (!ExtensionsHelper.MatchStart(ref slice, StartString, false)) { return(false); } if (slice.CurrentChar == '-') { slice.NextChar(); } string title = null, path = null; if (!ExtensionsHelper.MatchLink(ref slice, ref title, ref path) || !ExtensionsHelper.MatchInclusionEnd(ref slice)) { return(false); } processor.Inline = new InclusionInline { Title = title, IncludedFilePath = path, Line = line, Column = column, Span = new SourceSpan(startPosition, processor.GetSourcePosition(slice.Start - 1)), IsClosed = true, }; return(true); }
public override BlockState TryContinue(BlockProcessor processor, Block block) { if (processor.IsBlankLine) { return(BlockState.Continue); } var slice = processor.Line; var Row = (RowBlock)block; ExtensionsHelper.SkipSpaces(ref slice); if (!ExtensionsHelper.MatchStart(ref slice, new string(':', Row.ColonCount))) { return(BlockState.Continue); } ExtensionsHelper.SkipSpaces(ref slice); if (!ExtensionsHelper.MatchStart(ref slice, EndString, false)) { return(BlockState.Continue); } var c = ExtensionsHelper.SkipSpaces(ref slice); if (!c.IsZero()) { _context.LogWarning("invalid-row", $"Row has some invalid chars in the ending."); } block.UpdateSpanEnd(slice.End); return(BlockState.BreakDiscard); }
public override BlockState TryOpen(BlockProcessor processor) { if (processor.IsCodeIndent) { return(BlockState.None); } // Sample: [!code-javascript[Main](../jquery.js?name=testsnippet#tag "title")] var slice = processor.Line; if (!ExtensionsHelper.MatchStart(ref slice, StartString, false)) { return(BlockState.None); } var codeSnippet = new CodeSnippet(this); MatchLanguage(processor, ref slice, ref codeSnippet); if (!MatchName(processor, ref slice, ref codeSnippet)) { return(BlockState.None); } if (!MatchPath(processor, ref slice, ref codeSnippet)) { return(BlockState.None); } MatchQuery(processor, ref slice, ref codeSnippet); MatchTitle(processor, ref slice, ref codeSnippet); ExtensionsHelper.SkipWhitespace(ref slice); if (slice.CurrentChar == ')') { slice.NextChar(); ExtensionsHelper.SkipWhitespace(ref slice); if (slice.CurrentChar == ']') { var codeSnippetEnd = slice.Start; slice.NextChar(); ExtensionsHelper.SkipWhitespace(ref slice); if (slice.CurrentChar == '\0') { // slice finished its task, re-use it for Raw content slice.Start = processor.Line.Start; slice.End = codeSnippetEnd; codeSnippet.Raw = slice.ToString(); codeSnippet.Column = processor.Column; codeSnippet.Line = processor.LineIndex; processor.NewBlocks.Push(codeSnippet); return(BlockState.BreakDiscard); } } } return(BlockState.None); }
protected override void Write(HtmlRenderer renderer, InclusionInline inclusion) { if (string.IsNullOrEmpty(inclusion.Context.IncludedFilePath)) { Logger.LogError("file path can't be empty or null in IncludeFile"); renderer.Write(inclusion.Context.GetRaw()); return; } if (!PathUtility.IsRelativePath(inclusion.Context.IncludedFilePath)) { string tag = "ERROR INCLUDE"; string message = $"Unable to resolve {inclusion.Context.GetRaw()}: Absolute path \"{inclusion.Context.IncludedFilePath}\" is not supported."; ExtensionsHelper.GenerateNodeWithCommentWrapper(renderer, tag, message, inclusion.Context.GetRaw(), inclusion.Line); return; } var currentFilePath = ((RelativePath)_context.FilePath).GetPathFromWorkingFolder(); var includeFilePath = ((RelativePath)inclusion.Context.IncludedFilePath).BasedOn(currentFilePath); var filePath = Path.Combine(_context.BasePath, includeFilePath.RemoveWorkingFolder()); if (!File.Exists(filePath)) { Logger.LogWarning($"Can't find {includeFilePath}."); renderer.Write(inclusion.Context.GetRaw()); return; } var parents = _context.InclusionSet; if (parents != null && parents.Contains(includeFilePath)) { string tag = "ERROR INCLUDE"; string message = $"Unable to resolve {inclusion.Context.GetRaw()}: Circular dependency found in \"{_context.FilePath}\""; ExtensionsHelper.GenerateNodeWithCommentWrapper(renderer, tag, message, inclusion.Context.GetRaw(), inclusion.Line); return; } var content = File.ReadAllText(filePath); var context = new MarkdownContextBuilder() .WithContext(_context) .WithFilePath(includeFilePath.RemoveWorkingFolder()) .WithContent(content) .WithIsInline(true) .WithAddingIncludedFile(currentFilePath) .Build(); _engine.ReportDependency(includeFilePath); // Do not need to check if content is a single paragragh // context.IsInline = true will force it into a single paragragh and render with no <p></p> var result = _engine.Markup(context, _parameters); renderer.Write(result); }
public override BlockState TryOpen(BlockProcessor processor) { var slice = processor.Line; var column = processor.Column; var sourcePosition = processor.Start; if (processor.IsCodeIndent || !ExtensionsHelper.MatchStart(ref slice, ":::")) { return(BlockState.None); } ExtensionsHelper.SkipSpaces(ref slice); var extensionName = "triple-colon"; ITripleColonExtensionInfo extension; IDictionary <string, string> attributes; HtmlAttributes htmlAttributes; IDictionary <string, string> renderProperties; Action <string> logError = (string message) => _context.LogError( $"invalid-{extensionName}", $"Invalid {extensionName} on line {processor.LineIndex}. \"{slice.Text}\" is invalid. {message}", null, line: processor.LineIndex); if (!TryMatchIdentifier(ref slice, out extensionName) || !_extensions.TryGetValue(extensionName, out extension) || !extension.TryValidateAncestry(processor.CurrentContainer, logError) || !TryMatchAttributes(ref slice, out attributes, extensionName, extension.SelfClosing, logError) || !extension.TryProcessAttributes(attributes, out htmlAttributes, out renderProperties, logError)) { return(BlockState.None); } var block = new TripleColonBlock(this) { Closed = false, Column = column, Line = processor.LineIndex, Span = new SourceSpan(sourcePosition, slice.End), Extension = extension, RenderProperties = renderProperties }; if (htmlAttributes != null) { block.SetData(typeof(HtmlAttributes), htmlAttributes); } processor.NewBlocks.Push(block); if (extension.SelfClosing) { return(BlockState.BreakDiscard); } return(BlockState.ContinueDiscard); }
public override bool Match(InlineProcessor processor, ref StringSlice slice) { if (!ExtensionsHelper.MatchStart(ref slice, StartString, false)) { return(false); } var href = StringBuilderCache.Local(); var c = slice.CurrentChar; var saved = slice; var startChar = '\0'; int line; int column; if (c == '\'' || c == '"') { startChar = c; c = slice.NextChar(); } while (c != startChar && c != '>') { href.Append(c); c = slice.NextChar(); } if (startChar != '\0') { if (c != startChar) { return(false); } c = slice.NextChar(); } if (c != '>') { return(false); } slice.NextChar(); var xrefInline = new XrefInline { Href = href.ToString().Trim(), Span = new SourceSpan(processor.GetSourcePosition(saved.Start, out line, out column), processor.GetSourcePosition(slice.Start - 1)), Line = line, Column = column }; var htmlAttributes = xrefInline.GetAttributes(); htmlAttributes.AddPropertyIfNotExist("data-throw-if-not-resolved", "True"); processor.Inline = xrefInline; return(true); }
protected override void Write(HtmlRenderer renderer, InclusionBlock inclusion) { if (string.IsNullOrEmpty(inclusion.Context.IncludedFilePath)) { Logger.LogError("file path can't be empty or null in IncludeFile"); renderer.Write(inclusion.Context.GetRaw()); return; } if (!PathUtility.IsRelativePath(inclusion.Context.IncludedFilePath)) { var tag = "ERROR INCLUDE"; var message = $"Unable to resolve {inclusion.Context.GetRaw()}: Absolute path \"{inclusion.Context.IncludedFilePath}\" is not supported."; ExtensionsHelper.GenerateNodeWithCommentWrapper(renderer, tag, message, inclusion.Context.GetRaw(), inclusion.Line); return; } var currentFilePath = ((RelativePath)_context.FilePath).GetPathFromWorkingFolder(); var includedFilePath = ((RelativePath)inclusion.Context.IncludedFilePath).BasedOn(currentFilePath); if (!EnvironmentContext.FileAbstractLayer.Exists(includedFilePath)) { Logger.LogWarning($"Can't find {includedFilePath}."); renderer.Write(inclusion.Context.GetRaw()); return; } var parents = _context.InclusionSet; if (parents != null && parents.Contains(includedFilePath)) { string tag = "ERROR INCLUDE"; string message = $"Unable to resolve {inclusion.Context.GetRaw()}: Circular dependency found in \"{_context.FilePath}\""; ExtensionsHelper.GenerateNodeWithCommentWrapper(renderer, tag, message, inclusion.Context.GetRaw(), inclusion.Line); return; } var content = EnvironmentContext.FileAbstractLayer.ReadAllText(includedFilePath); var context = new MarkdownContextBuilder() .WithContext(_context) .WithFilePath(includedFilePath.RemoveWorkingFolder()) .WithContent(content) .WithAddingIncludedFile(currentFilePath) .Build(); _engine.ReportDependency(includedFilePath); var result = _engine.Markup(context, _parameters); result = SkipYamlHeader(result); renderer.Write(result); }
public override BlockState TryContinue(BlockProcessor processor, Block block) { var slice = processor.Line; if (processor.IsBlankLine) { return(BlockState.Continue); } ExtensionsHelper.SkipSpaces(ref slice); if (!ExtensionsHelper.MatchStart(ref slice, ":::")) { ExtensionsHelper.ResetLineIndent(processor); return(BlockState.Continue); } ExtensionsHelper.SkipSpaces(ref slice); var extensionName = ((TripleColonBlock)block).Extension.Name; if (!ExtensionsHelper.MatchStart(ref slice, extensionName) || !ExtensionsHelper.MatchStart(ref slice, "-end")) { ExtensionsHelper.ResetLineIndent(processor); return(BlockState.Continue); } var c = ExtensionsHelper.SkipSpaces(ref slice); var endingTripleColons = ((TripleColonBlock)block).EndingTripleColons; if (endingTripleColons && !ExtensionsHelper.MatchStart(ref slice, ":::")) { _context.LogWarning( $"invalid-{extensionName}", $"Invalid {extensionName} on line {block.Line}. \"{slice.Text}\" is invalid. Missing ending \":::{extensionName}-end:::\"", block); return(BlockState.Continue); } if (!c.IsZero() && !endingTripleColons) { _context.LogWarning( $"invalid-{extensionName}", $"Invalid {extensionName} on line {block.Line}. \"{slice.Text}\" is invalid. Invalid character after \"::: {extensionName}-end\": \"{c}\"", block); } block.UpdateSpanEnd(slice.End); block.IsOpen = false; (block as TripleColonBlock).Closed = true; return(BlockState.BreakDiscard); }
public override BlockState TryOpen(BlockProcessor processor) { if (processor.IsCodeIndent) { return(BlockState.None); } // [!include[<title>](<filepath>)] var column = processor.Column; var line = processor.Line; var command = line.ToString(); if (!ExtensionsHelper.MatchStart(ref line, StartString, false)) { return(BlockState.None); } else { if (line.CurrentChar == '+') { line.NextChar(); } } string title = null, path = null; if (!ExtensionsHelper.MatchLink(ref line, ref title, ref path) || !ExtensionsHelper.MatchInlcusionEnd(ref line)) { return(BlockState.None); } while (line.CurrentChar.IsSpaceOrTab()) { line.NextChar(); } if (line.CurrentChar != '\0') { return(BlockState.None); } processor.NewBlocks.Push(new InclusionBlock(this) { Title = title, IncludedFilePath = path, Line = processor.LineIndex, Column = column, }); return(BlockState.BreakDiscard); }
protected override void Write(HtmlRenderer renderer, TabGroupBlock block) { renderer.Write(@"<div class=""tabGroup"" id=""tabgroup_"); var groupId = ExtensionsHelper.Escape(block.Id); renderer.Write(groupId); renderer.Write("\""); renderer.WriteAttributes(block); renderer.Write(">\n"); WriteTabHeaders(renderer, block, groupId); WriteTabSections(renderer, block, groupId); renderer.Write("</div>\n"); }
public override BlockState TryOpen(BlockProcessor processor) { if (processor.IsCodeIndent) { return(BlockState.None); } var slice = processor.Line; if (ExtensionsHelper.IsEscaped(slice)) { return(BlockState.None); } var column = processor.Column; var sourcePosition = processor.Start; var colonCount = 0; var c = slice.CurrentChar; while (c == Colon) { c = slice.NextChar(); colonCount++; } if (colonCount < 3) { return(BlockState.None); } ExtensionsHelper.SkipSpaces(ref slice); if (!ExtensionsHelper.MatchStart(ref slice, StartString, false)) { return(BlockState.None); } processor.NewBlocks.Push(new RowBlock(this) { ColonCount = colonCount, Column = column, Span = new SourceSpan(sourcePosition, slice.End), }); return(BlockState.ContinueDiscard); }
public override BlockState TryOpen(BlockProcessor processor) { if (processor.IsCodeIndent) { return(BlockState.None); } // [!include[<title>](<filepath>)] var column = processor.Column; var line = processor.Line; var command = line.ToString(); var includeFile = new InclusionBlock(this); if (!ExtensionsHelper.MatchStart(ref line, StartString, false)) { return(BlockState.None); } else { if (line.CurrentChar == '+') { line.NextChar(); } } var context = new InclusionContext(); if (!ExtensionsHelper.MatchLink(ref line, ref context)) { return(BlockState.None); } while (line.CurrentChar.IsSpaceOrTab()) { line.NextChar(); } if (line.CurrentChar != '\0') { return(BlockState.None); } includeFile.Context = context; processor.NewBlocks.Push(includeFile); return(BlockState.BreakDiscard); }
public override bool Match(InlineProcessor processor, ref StringSlice slice) { if (!ExtensionsHelper.MatchStart(ref slice, ":::")) { return(false); } if (!TripleColonBlockParser.TryMatchIdentifier(ref slice, out var extensionName) || !_extensions.TryGetValue(extensionName, out var extension)) { return(false); } var inline = new TripleColonInline(this) { Closed = false, Column = 0, Line = processor.LineIndex, Span = new SourceSpan(processor.LineIndex, slice.End), }; var logError = new Action <string>(message => _context.LogError($"invalid-{extensionName}", message, inline)); var logWarning = new Action <string>(message => _context.LogWarning($"invalid-{extensionName}", message, inline)); if (!TripleColonBlockParser.TryMatchAttributes(ref slice, out var attributes, extension.SelfClosing, logError) || !extension.TryProcessAttributes(attributes, out var htmlAttributes, out var renderProperties, logError, logWarning, inline)) { return(false); } inline.Extension = extension; inline.Attributes = attributes; inline.RenderProperties = renderProperties; if (htmlAttributes != null) { inline.SetData(typeof(HtmlAttributes), htmlAttributes); } processor.Inline = inline; return(true); }
private bool TryMatchAttributes(ref StringSlice slice, out IDictionary <string, string> attributes, string extensionName, bool selfClosing, Action <string> logError) { attributes = EmptyAttributes; while (true) { ExtensionsHelper.SkipSpaces(ref slice); if (slice.CurrentChar.IsZero() || (selfClosing && ExtensionsHelper.MatchStart(ref slice, ":::"))) { return(true); } if (!TryMatchIdentifier(ref slice, out var attributeName)) { logError("Invalid attribute."); return(false); } if (attributes.ContainsKey(attributeName)) { logError($"Attribute \"{attributeName}\" specified multiple times."); return(false); } var value = string.Empty; ExtensionsHelper.SkipSpaces(ref slice); if (slice.CurrentChar == '=') { slice.NextChar(); ExtensionsHelper.SkipSpaces(ref slice); if (!TryMatchAttributeValue(ref slice, out value, extensionName, attributeName, logError)) { return(false); } } if (attributes == EmptyAttributes) { attributes = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); } attributes.Add(attributeName, value); } }
private static string GetCodeSnippet(string range, string id, string code, Action <string> logError) { if (!string.IsNullOrEmpty(range) && !string.IsNullOrEmpty(id)) { logError("You must set only either Range or Id, but not both."); } var codeSections = new List <string>(); if (!string.IsNullOrEmpty(range)) { var codeLines = code.Split('\n'); codeSections = GetCodeSectionsFromRange(range, codeLines, codeSections, logError); } else if (!string.IsNullOrEmpty(id)) { var codeLines = code.Split('\n'); var beg = codeLines.FindIndexOfTag(id); var end = codeLines.FindIndexOfTag(id, true); if (end == 0) { logError($"Could not find snippet id '{id}'. Make sure your snippet is in your source file."); } codeSections = GetCodeSectionsFromRange($"{beg}-{end}", codeLines, codeSections, logError, false); } else { codeSections.Add(code); } if (codeSections == null) { return(string.Empty); } codeSections = Dedent(codeSections); var source = string.Join("\n", codeSections.ToArray()); source = ExtensionsHelper.Escape(source); return(source); }
private bool MatchPath(BlockProcessor processor, ref StringSlice slice, ref CodeSnippet codeSnippet) { ExtensionsHelper.SkipWhitespace(ref slice); if (slice.CurrentChar != '(') { return(false); } var c = slice.NextChar(); var bracketNeedToMatch = 0; var path = StringBuilderCache.Local(); var hasEscape = false; while (c != '\0' && (hasEscape || (c != '#' && c != '?' && c != '"' && (c != ')' || bracketNeedToMatch > 0)))) { if (c == '\\' && !hasEscape) { hasEscape = true; } else { if (c == '(' && !hasEscape) { bracketNeedToMatch++; } if (c == ')' && !hasEscape) { bracketNeedToMatch--; } path.Append(c); hasEscape = false; } c = slice.NextChar(); } codeSnippet.CodePath = path.ToString().Trim(); return(true); }
public bool Render(HtmlRenderer renderer, MarkdownObject markdownObject, Action <string> logWarning) { var block = (TripleColonBlock)markdownObject; block.Attributes.TryGetValue("id", out var currentId); //it's okay if this is null block.Attributes.TryGetValue("range", out var currentRange); //it's okay if this is null block.Attributes.TryGetValue("source", out var currentSource); //source has already been checked above var(code, codePath) = _context.ReadFile(currentSource, block); if (string.IsNullOrEmpty(code)) { logWarning($"The code snippet \"{currentSource}\" could not be found."); return(false); } //var updatedCode = GetCodeSnippet(currentRange, currentId, code, logError).TrimEnd(); var htmlCodeSnippetRenderer = new HtmlCodeSnippetRenderer(_context); var snippet = new CodeSnippet(null); snippet.CodePath = currentSource; snippet.TagName = currentId; HtmlCodeSnippetRenderer.TryGetLineRanges(currentRange, out var ranges); snippet.CodeRanges = ranges; var updatedCode = htmlCodeSnippetRenderer.GetContent(code, snippet); updatedCode = ExtensionsHelper.Escape(updatedCode).TrimEnd(); if (updatedCode == string.Empty) { logWarning($"It looks like your code snippet was not rendered. Try range instead."); return(false); } renderer.WriteLine("<pre>"); renderer.Write("<code").WriteAttributes(block).Write(">"); renderer.WriteLine(updatedCode); renderer.WriteLine("</code></pre>"); return(true); }
protected override void Write(HtmlRenderer renderer, CodeSnippet codeSnippet) { var refFileRelativePath = ((RelativePath)codeSnippet.CodePath).BasedOn((RelativePath)_context.FilePath); if (!EnvironmentContext.FileAbstractLayer.Exists(refFileRelativePath)) { string tag = "ERROR CODESNIPPET"; string message = $"Unable to find {refFileRelativePath}"; ExtensionsHelper.GenerateNodeWithCommentWrapper(renderer, tag, message, codeSnippet.Raw, codeSnippet.Line); return; } if (codeSnippet.DedentLength != null && codeSnippet.DedentLength < 0) { renderer.Write($"<!-- Dedent length {codeSnippet.DedentLength} should be positive. Auto-dedent will be applied. -->\n"); } codeSnippet.SetAttributeString(); renderer.Write("<pre><code").WriteAttributes(codeSnippet).Write(">"); renderer.WriteEscape(GetContent(codeSnippet)); renderer.Write("</code></pre>"); }
private static string GetCodeSnippet(string range, string id, string code, Action <string> logError) { if (!string.IsNullOrEmpty(range) && !string.IsNullOrEmpty(id)) { logError($"You must set only either Range or Id, but not both."); } var codeSections = new List <string>(); if (!string.IsNullOrEmpty(range)) { var codeLines = code.Split('\n'); codeSections = GetCodeSectionsFromRange(range, codeLines, codeSections, logError); } else if (!string.IsNullOrEmpty(id)) { var codeLines = code.Split('\n'); var beg = Array.FindIndex(codeLines, line => line.Replace(" ", "").IndexOf($"<{id}>", StringComparison.OrdinalIgnoreCase) > -1) + 2; var end = Array.FindIndex(codeLines, line => line.Replace(" ", "").IndexOf($"</{id}>", StringComparison.OrdinalIgnoreCase) > -1); codeSections = GetCodeSectionsFromRange($"{beg}-{end}", codeLines, codeSections, logError); } else { codeSections.Add(code); } if (codeSections == null) { return(string.Empty); } codeSections = Dedent(codeSections); var source = string.Join(" ...\n", codeSections.ToArray()); source = ExtensionsHelper.Escape(source); return(source); }
public override BlockState TryOpen(BlockProcessor processor) { var slice = processor.Line; var sourcePosition = processor.Start; if (processor.IsCodeIndent || !ExtensionsHelper.MatchStart(ref slice, ":::")) { return(BlockState.None); } ExtensionsHelper.SkipSpaces(ref slice); var extensionName = "triple-colon"; Action <string> logError = (string message) => _context.LogError( $"invalid-{extensionName}", $"{message}", null, line: processor.LineIndex); Action <string> logWarning = (string message) => _context.LogWarning( $"invalid-{extensionName}", $"{message}", null, line: processor.LineIndex); var block = new TripleColonBlock(this) { Closed = false, Column = processor.Column, Line = processor.LineIndex, Span = new SourceSpan(sourcePosition, slice.End), }; if (!TryMatchIdentifier(ref slice, out extensionName) || !_extensions.TryGetValue(extensionName, out var extension) || !extension.TryValidateAncestry(processor.CurrentContainer, logError) || !TryMatchAttributes(ref slice, out var attributes, extensionName, extension.SelfClosing, logError) || !extension.TryProcessAttributes(attributes, out var htmlAttributes, out var renderProperties, logError, logWarning, block)) { return(BlockState.None); } block.Extension = extension; block.Attributes = attributes; block.RenderProperties = renderProperties; if (htmlAttributes != null) { block.SetData(typeof(HtmlAttributes), htmlAttributes); } processor.NewBlocks.Push(block); if (extension.GetType() == typeof(ImageExtension) && htmlAttributes != null && ImageExtension.RequiresClosingTripleColon(attributes)) { block.EndingTripleColons = true; return(BlockState.ContinueDiscard); } if (extension.SelfClosing) { return(BlockState.BreakDiscard); } return(BlockState.ContinueDiscard); }
public bool TryProcessAttributes(IDictionary <string, string> attributes, out HtmlAttributes htmlAttributes, out IDictionary <string, string> renderProperties, Action <string> logError, Action <string> logWarning, TripleColonBlock block) { htmlAttributes = null; renderProperties = new Dictionary <string, string>(); var source = string.Empty; var range = string.Empty; var id = string.Empty; var highlight = string.Empty; var language = string.Empty; var interactive = string.Empty; foreach (var attribute in attributes) { var name = attribute.Key; var value = attribute.Value; switch (name) { case "source": source = value; break; case "range": range = value; break; case "id": id = value; break; case "highlight": highlight = value; break; case "language": language = value; break; case "interactive": interactive = value; break; default: logError($"Unexpected attribute \"{name}\"."); return(false); } } if (string.IsNullOrEmpty(source)) { logError("source is a required attribute. Please ensure you have specified a source attribute"); return(false); } if (string.IsNullOrEmpty(language)) { language = InferLanguageFromFile(source, logError); } htmlAttributes = new HtmlAttributes(); htmlAttributes.AddProperty("class", $"lang-{language}"); if (!string.IsNullOrEmpty(interactive)) { htmlAttributes.AddProperty("data-interactive", language); htmlAttributes.AddProperty("data-interactive-mode", interactive); } if (!string.IsNullOrEmpty(highlight)) { htmlAttributes.AddProperty("highlight-lines", highlight); } RenderDelegate = (renderer, obj) => { var currentId = string.Empty; var currentRange = string.Empty; var currentSource = string.Empty; obj.Attributes.TryGetValue("id", out currentId); //it's okay if this is null obj.Attributes.TryGetValue("range", out currentRange); //it's okay if this is null obj.Attributes.TryGetValue("source", out currentSource); //source has already been checked above var(code, codePath) = _context.ReadFile(currentSource, obj); if (string.IsNullOrEmpty(code)) { logWarning($"The code snippet \"{currentSource}\" could not be found."); return(false); } //var updatedCode = GetCodeSnippet(currentRange, currentId, code, logError).TrimEnd(); var htmlCodeSnippetRenderer = new HtmlCodeSnippetRenderer(_context); var snippet = new CodeSnippet(null); snippet.CodePath = source; snippet.TagName = currentId; List <CodeRange> ranges; HtmlCodeSnippetRenderer.TryGetLineRanges(currentRange, out ranges); snippet.CodeRanges = ranges; var updatedCode = htmlCodeSnippetRenderer.GetContent(code, snippet); updatedCode = ExtensionsHelper.Escape(updatedCode).TrimEnd(); if (updatedCode == string.Empty) { return(false); } renderer.WriteLine("<pre>"); renderer.Write("<code").WriteAttributes(obj).Write(">"); renderer.WriteLine(updatedCode); renderer.WriteLine("</code></pre>"); return(true); }; return(true); }
public override BlockState TryOpen(BlockProcessor processor) { if (processor.IsBlankLine) { return(BlockState.Continue); } var slice = processor.Line; if (ExtensionsHelper.IsEscaped(slice)) { return(BlockState.None); } var column = processor.Column; var sourcePosition = processor.Start; var colonCount = 0; ExtensionsHelper.SkipSpaces(ref slice); var columnWidth = StringBuilderCache.Local(); var c = slice.CurrentChar; while (c == Colon) { c = slice.NextChar(); colonCount++; } if (colonCount < 3) { return(BlockState.None); } ExtensionsHelper.SkipSpaces(ref slice); if (!ExtensionsHelper.MatchStart(ref slice, StartString, false)) { return(BlockState.None); } ExtensionsHelper.SkipSpaces(ref slice); if (ExtensionsHelper.MatchStart(ref slice, "span=\"", false)) { c = slice.CurrentChar; while (c != '"') { columnWidth.Append(c); c = slice.NextChar(); } if (!ExtensionsHelper.MatchStart(ref slice, "\"", false)) { return(BlockState.None); } } else { columnWidth.Append("1"); // default span is one } while (c.IsSpace()) { c = slice.NextChar(); } if (!ExtensionsHelper.MatchStart(ref slice, ":::", false)) //change { return(BlockState.None); } if (!c.IsZero()) { Logger.LogWarning($"NestedColumn have some invalid chars in the starting."); } processor.NewBlocks.Push(new NestedColumnBlock(this) { ColumnWidth = columnWidth.ToString(), ColonCount = colonCount, Column = column, Span = new SourceSpan(sourcePosition, slice.End), }); return(BlockState.ContinueDiscard); }
public override BlockState TryOpen(BlockProcessor processor) { if (processor.IsCodeIndent) { return(BlockState.None); } var slice = processor.Line; var column = processor.Column; var sourcePosition = processor.Start; var colonCount = 0; var c = slice.CurrentChar; while (c == Colon) { c = slice.NextChar(); colonCount++; } if (colonCount < 3) { return(BlockState.None); } ExtensionsHelper.SkipSpaces(ref slice); if (!ExtensionsHelper.MatchStart(ref slice, "moniker", false)) { return(BlockState.None); } ExtensionsHelper.SkipSpaces(ref slice); if (!ExtensionsHelper.MatchStart(ref slice, "range=\"", false)) { return(BlockState.None); } var range = StringBuilderCache.Local(); c = slice.CurrentChar; while (c != '\0' && c != '"') { range.Append(c); c = slice.NextChar(); } if (c != '"') { _context.LogWarning("invalid-moniker-range", "MonikerRange does not have ending charactor (\")."); return(BlockState.None); } c = slice.NextChar(); while (c.IsSpace()) { c = slice.NextChar(); } if (!c.IsZero()) { _context.LogWarning("invalid-moniker-range", $"MonikerRange have some invalid chars in the starting."); } var monikerRange = new MonikerRangeBlock(this) { Closed = false, MonikerRange = range.ToString(), ColonCount = colonCount, Column = column, Span = new SourceSpan(sourcePosition, slice.End), }; monikerRange.GetAttributes().AddPropertyIfNotExist("range", monikerRange.MonikerRange); processor.NewBlocks.Push(monikerRange); return(BlockState.ContinueDiscard); }
public override BlockState TryOpen(BlockProcessor processor) { if (processor.IsCodeIndent) { return(BlockState.None); } var slice = processor.Line; if (ExtensionsHelper.IsEscaped(slice)) { return(BlockState.None); } var column = processor.Column; var sourcePosition = processor.Start; var colonCount = 0; var c = slice.CurrentChar; while (c == Colon) { c = slice.NextChar(); colonCount++; } if (colonCount < 3) { return(BlockState.None); } ExtensionsHelper.SkipSpaces(ref slice); if (!ExtensionsHelper.MatchStart(ref slice, "moniker", false)) { return(BlockState.None); } ExtensionsHelper.SkipSpaces(ref slice); if (!ExtensionsHelper.MatchStart(ref slice, "range=\"", false)) { return(BlockState.None); } var range = StringBuilderCache.Local(); c = slice.CurrentChar; while (c != '\0' && c != '"') { range.Append(c); c = slice.NextChar(); } if (c != '"') { return(BlockState.None); } c = slice.NextChar(); while (c.IsSpace()) { c = slice.NextChar(); } if (!c.IsZero()) { Logger.LogWarning($"MonikerRange have some invalid chars in the starting."); } processor.NewBlocks.Push(new MonikerRangeBlock(this) { MonikerRange = range.ToString(), ColonCount = colonCount, Column = column, Span = new SourceSpan(sourcePosition, slice.End), }); return(BlockState.ContinueDiscard); }
public override BlockState TryOpen(BlockProcessor processor) { if (processor.IsCodeIndent) { return(BlockState.None); } var slice = processor.Line; if (ExtensionsHelper.IsEscaped(slice)) { return(BlockState.None); } var column = processor.Column; var sourcePosition = processor.Start; var colonCount = 0; var c = slice.CurrentChar; while (c == Colon) { c = slice.NextChar(); colonCount++; } if (colonCount < 3) { return(BlockState.None); } ExtensionsHelper.SkipSpaces(ref slice); if (!ExtensionsHelper.MatchStart(ref slice, StartString, false)) { return(BlockState.None); } ExtensionsHelper.SkipSpaces(ref slice); if (!ExtensionsHelper.MatchStart(ref slice, "render=\"", false)) { return(BlockState.None); } var range = StringBuilderCache.Local(); c = slice.CurrentChar; while (c != '\0' && c != '"') { range.Append(c); c = slice.NextChar(); } if (c != '"') { _context.LogWarning("invalid-render-zone", "Zone render does not have ending character (\")."); return(BlockState.None); } c = slice.NextChar(); while (c.IsSpace()) { c = slice.NextChar(); } if (!c.IsZero()) { _context.LogWarning("invalid-render-zone", $"Zone render has some invalid chars in the beginning."); } // Check the blockprocessor context to see if we are already inside of a zone // container. If so, break. var containerBlock = processor.CurrentContainer; do { if (processor.CurrentContainer.GetType() == typeof(RenderZoneBlock)) { _context.LogError("invalid-render-zone", "Zone render cannot be nested."); return(BlockState.None); } containerBlock = containerBlock.Parent; } while (containerBlock != null); processor.NewBlocks.Push(new RenderZoneBlock(this) { Closed = false, ColonCount = colonCount, Column = column, Span = new SourceSpan(sourcePosition, slice.End), Target = range.ToString(), }); return(BlockState.ContinueDiscard); }