private async Task <InsertingStreamLocation> ExecuteShortcodesAsync(
            IDocument input,
            IExecutionContext context,
            ShortcodeLocation location,
            ImmutableDictionary <string, IShortcode> shortcodes)
        {
            // Execute the shortcode
            IDocument shortcodeResult = await shortcodes[location.Name].ExecuteAsync(location.Arguments, location.Content, input, context);
            IDocument mergedResult    = null;

            if (shortcodeResult != null)
            {
                // Merge output metadata with the current input document
                // Creating a new document is the easiest way to ensure all the metadata from shortcodes gets accumulated correctly
                mergedResult = input.Clone(shortcodeResult, shortcodeResult.ContentProvider);

                // Don't process nested shortcodes if it's the raw shortcode
                if (!location.Name.Equals(RawShortcode.RawShortcodeName, StringComparison.OrdinalIgnoreCase))
                {
                    // Recursively parse shortcodes
                    IDocument nestedResult = await ProcessShortcodesAsync(mergedResult, context);

                    if (nestedResult != null)
                    {
                        mergedResult = nestedResult;
                    }
                }
                return(new InsertingStreamLocation(location.FirstIndex, location.LastIndex, mergedResult));
            }

            return(new InsertingStreamLocation(location.FirstIndex, location.LastIndex, mergedResult));
        }
예제 #2
0
        private async Task <InsertingStreamLocation> ExecuteShortcodesAsync(
            IDocument input,
            IExecutionContext context,
            ShortcodeLocation location,
            ImmutableDictionary <string, IShortcode> shortcodes)
        {
            // Execute the shortcode
            IEnumerable <ShortcodeResult> results = await shortcodes[location.Name].ExecuteAsync(location.Arguments, location.Content, input, context);

            // Process the results
            if (results != null)
            {
                // Iterate the result content streams
                List <IContentProvider> resultContentProviders = new List <IContentProvider>();
                foreach (ShortcodeResult result in results)
                {
                    if (result != null && result.ContentProvider != null)
                    {
                        // Don't process nested shortcodes if it's the raw shortcode
                        IContentProvider resultContentProvider = result.ContentProvider;
                        if (!location.Name.Equals(RawShortcode.RawShortcodeName, StringComparison.OrdinalIgnoreCase))
                        {
                            // Clone the input document with nested metadata if we have any
                            IDocument nestedInput = result.NestedMetadata?.Count > 0 ? input.Clone(result.NestedMetadata) : input;

                            // Recursively parse shortcodes
                            IContentProvider nestedContentProvider = await ProcessShortcodesAsync(nestedInput, result.ContentProvider, context);

                            if (nestedContentProvider != null)
                            {
                                resultContentProvider = nestedContentProvider;
                            }
                        }

                        resultContentProviders.Add(resultContentProvider);
                    }
                }

                if (resultContentProviders.Count > 0)
                {
                    return(new InsertingStreamLocation(location.FirstIndex, location.LastIndex, resultContentProviders));
                }
            }

            return(new InsertingStreamLocation(location.FirstIndex, location.LastIndex, null));
        }
예제 #3
0
        /// <summary>
        /// Identifies shortcode locations in a stream.
        /// </summary>
        /// <param name="stream">The stream to parse. This method will not dispose the passed-in stream.</param>
        /// <returns>All of the shortcode locations in the stream.</returns>
        public List <ShortcodeLocation> Parse(Stream stream)
        {
            List <ShortcodeLocation> locations = new List <ShortcodeLocation>();

            CurrentTag        currentTag = null;
            ShortcodeLocation shortcode  = null;
            StringBuilder     content    = null;

            using (TextReader reader = new StreamReader(stream, Encoding.UTF8, true, 1024, true))
            {
                int r;
                int i = 0;
                while ((r = reader.Read()) != -1)
                {
                    char c = (char)r;

                    // Look for delimiters and tags
                    if (currentTag == null && shortcode == null)
                    {
                        // Searching for open tag start delimiter
                        if (_startDelimiter.Locate(c, false))
                        {
                            currentTag = new CurrentTag(i - (_startDelimiter.Text.Length - 1));
                        }
                    }
                    else if (currentTag != null && shortcode == null)
                    {
                        // Searching for open tag end delimiter
                        currentTag.Content.Append(c);
                        if (_endDelimiter.Locate(c, false))
                        {
                            // Is this self-closing?
                            if (currentTag.Content[currentTag.Content.Length - _endDelimiter.Text.Length - 1] == '/')
                            {
                                // Self-closing
                                shortcode = GetShortcodeLocation(
                                    currentTag.FirstIndex,
                                    currentTag.Content.ToString(0, currentTag.Content.Length - _endDelimiter.Text.Length - 1));
                                shortcode.Finish(i);
                                locations.Add(shortcode);
                                shortcode = null;
                            }
                            else
                            {
                                // Look for a closing tag
                                shortcode = GetShortcodeLocation(
                                    currentTag.FirstIndex,
                                    currentTag.Content.ToString(0, currentTag.Content.Length - _endDelimiter.Text.Length));
                                content = new StringBuilder();
                            }

                            currentTag = null;
                        }
                    }
                    else if (currentTag == null && shortcode != null)
                    {
                        content.Append(c);

                        // Searching for close tag start delimiter
                        if (_startDelimiter.Locate(c, true))
                        {
                            currentTag = new CurrentTag(i);
                        }
                    }
                    else
                    {
                        currentTag.Content.Append(c);

                        // Searching for close tag end delimiter
                        if (_endDelimiter.Locate(c, false))
                        {
                            // Get the name of this shortcode close tag
                            string name = currentTag.Content.ToString(
                                0,
                                currentTag.Content.Length - _endDelimiter.Text.Length)
                                          .Trim();
                            if (name.Any(x => char.IsWhiteSpace(x)))
                            {
                                throw new ShortcodeParserException("Closing shortcode tags should only consist of the shortcode name");
                            }

                            // Make sure it's the same name
                            if (name.Equals(shortcode.Name, StringComparison.OrdinalIgnoreCase))
                            {
                                // If the content is contained within a processing instruction, trim that
                                string shortcodeContent = content.ToString(0, content.Length - _startDelimiter.Text.Length - 1);
                                string trimmedContent   = shortcodeContent.Trim();
                                if (trimmedContent.StartsWith(TrimStartDelimiter) && trimmedContent.EndsWith(TrimEndDelimiter))
                                {
                                    shortcodeContent = trimmedContent.Substring(
                                        TrimStartDelimiter.Length,
                                        trimmedContent.Length - (TrimStartDelimiter.Length + TrimEndDelimiter.Length));
                                }

                                shortcode.Content = shortcodeContent;
                                shortcode.Finish(i);
                                locations.Add(shortcode);

                                shortcode = null;
                                content   = null;
                            }
                            else
                            {
                                // It wasn't the same name, so add the tag content to the running content
                                content.Append(currentTag.Content.ToString());
                            }

                            currentTag = null;
                        }
                    }

                    i++;
                }

                if (shortcode != null)
                {
                    throw new ShortcodeParserException($"The shortcode {shortcode.Name} was not terminated");
                }
            }

            return(locations);
        }