Пример #1
0
        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);
        }
Пример #2
0
        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);
        }
Пример #3
0
        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);
        }
Пример #4
0
        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);
        }
Пример #5
0
        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);
        }
Пример #6
0
        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);
        }
Пример #7
0
        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);
        }
Пример #8
0
        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);
        }
Пример #9
0
        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);
        }
Пример #10
0
        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);
        }
Пример #11
0
        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);
        }
Пример #12
0
        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);
        }
Пример #13
0
        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);
        }
Пример #14
0
        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");
        }
Пример #15
0
        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);
        }
Пример #16
0
        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);
        }
Пример #17
0
        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);
        }
Пример #18
0
        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);
            }
        }
Пример #19
0
        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);
        }
Пример #20
0
        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);
        }
Пример #21
0
        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);
        }
Пример #22
0
        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>");
        }
Пример #23
0
        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);
        }
Пример #24
0
        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);
        }
Пример #25
0
        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);
        }
Пример #26
0
        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);
        }
Пример #27
0
        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);
        }
Пример #28
0
        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);
        }
Пример #29
0
        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);
        }