Пример #1
0
        public static IEnumerable <object[]> FlexiIncludeBlockOptions_CanBePopulated_Data()
        {
            const string           dummySource         = "dummySource";
            const FlexiIncludeType dummyType           = FlexiIncludeType.Markdown;
            const bool             dummyCache          = false;
            const string           dummyCacheDirectory = "dummyCacheDirectory";

            var dummyClipping1  = new Clipping(10, 15);
            var dummyClippings1 = new List <Clipping> {
                dummyClipping1
            };
            var dummyClipping2  = new Clipping(2, 21);
            var dummyClippings2 = new List <Clipping> {
                dummyClipping2
            };

            return(new object[][]
            {
                // Populating FlexiIncludeBlockOptions containing default values
                new object[]
                {
                    new SerializableWrapper <FlexiIncludeBlockOptions>(new FlexiIncludeBlockOptions()),
                    new SerializableWrapper <FlexiIncludeBlockOptions>(new FlexiIncludeBlockOptions(dummySource,
                                                                                                    dummyClippings1,
                                                                                                    dummyType,
                                                                                                    dummyCache,
                                                                                                    dummyCacheDirectory)),
                    $@"{{
    ""{nameof(FlexiIncludeBlockOptions.Source)}"": ""{dummySource}"",
    ""{nameof(FlexiIncludeBlockOptions.Type)}"": ""{dummyType}"",
    ""{nameof(FlexiIncludeBlockOptions.Cache)}"": ""{dummyCache}"",
    ""{nameof(FlexiIncludeBlockOptions.CacheDirectory)}"": ""{dummyCacheDirectory}"",
    ""{nameof(FlexiIncludeBlockOptions.Clippings)}"": [
        {{
            ""{nameof(Clipping.StartLine)}"": ""{dummyClipping1.StartLine}"",
            ""{nameof(Clipping.EndLine)}"": ""{dummyClipping1.EndLine}""
        }}
    ]
}}"
                },

                // Populating FlexiIncludeBlockOptions with an existing clippings collection (should be replaced instead of appended to)
                new object[]
                {
                    new SerializableWrapper <FlexiIncludeBlockOptions>(new FlexiIncludeBlockOptions(clippings: dummyClippings1)),
                    new SerializableWrapper <FlexiIncludeBlockOptions>(new FlexiIncludeBlockOptions(clippings: dummyClippings2)),
                    $@"{{
    ""{nameof(FlexiIncludeBlockOptions.Clippings)}"": [
        {{
            ""{nameof(Clipping.StartLine)}"": ""{dummyClipping2.StartLine}"",
            ""{nameof(Clipping.EndLine)}"": ""{dummyClipping2.EndLine}""
        }}
    ]
}}"
                }
            });
        }
Пример #2
0
 internal virtual void ValidateType(FlexiIncludeType type)
 {
     if (!Enum.IsDefined(typeof(FlexiIncludeType), type))
     {
         throw new OptionsException(nameof(IFlexiIncludeBlockOptions.Type),
                                    string.Format(Strings.OptionsException_Shared_ValueMustBeAValidEnumValue,
                                                  type,
                                                  nameof(FlexiIncludeType)));
     }
 }
 /// <summary>
 /// Creates a <see cref="FlexiIncludeBlockOptions"/>.
 /// </summary>
 /// <param name="source">
 /// <para>The <see cref="FlexiIncludeBlock"/>'s source.</para>
 /// <para>This value must either be a relative URI or an absolute URI with scheme file, http or https.</para>
 /// <para>If this value is a relative URI and the <see cref="FlexiIncludeBlock"/> is in root content, <see cref="IFlexiIncludeBlocksExtensionOptions.BaseUri"/>
 /// is used as the base URI.</para>
 /// <para>If this value is a relative URI and the <see cref="FlexiIncludeBlock"/> is in non-root content, the absolute URI of its containing source is used as the base URI.</para>
 /// <para>For example, consider standard Markdig usage: <c>string html = Markdown.ToHtml(rootContent, yourMarkdownPipeline);</c>.</para>
 /// <para>To Markdig, root content has no associated source, it is just a string containing markup.
 /// To work around this limitation, if the root content contains a <see cref="FlexiIncludeBlock"/> with a relative URI source like "../my/path/file1.md", <see cref="FlexiIncludeBlocksExtension"/>
 /// uses <see cref="IFlexiIncludeBlocksExtensionOptions.BaseUri"/> to resolve the absolute URI of "file1.md".</para>
 /// <para>As such, <see cref="IFlexiIncludeBlocksExtensionOptions.BaseUri"/> is typically configured as the absolute URI of the root source.</para>
 /// <para>If "file1.md" contains a FlexiIncludeBlock with source "../my/path/file2.md", we use the previously resolved absolute URI of "file1.md" as the base URI to
 /// resolve the absolute URI of "file2.md".</para>
 /// <para>Note that retrieving content from remote sources can introduce security issues. As far as possible, retrieve remote content only from trusted or permanent links. For example,
 /// from <a href="https://help.github.com/articles/getting-permanent-links-to-files/">Github permalink</a>s. Additionally, consider sanitizing generated HTML.</para>
 /// <para>Defaults to <see cref="string.Empty"/>.</para>
 /// </param>
 /// <param name="clippings">
 /// <para>The <see cref="Clipping"/>s specifying content from the source to include.</para>
 /// <para>If this value is <c>null</c> or empty, all content from the source is included.</para>
 /// <para>Defaults to <c>null</c>.</para>
 /// </param>
 /// <param name="type">
 /// <para>The <see cref="FlexiIncludeBlock"/>'s type.</para>
 /// <para>If this value is <see cref="FlexiIncludeType.Code"/>, the included content is rendered in a code block.
 /// If this value is <see cref="FlexiIncludeType.Markdown"/>, the included content is processed as markdown.</para>
 /// <para>Defaults to <see cref="FlexiIncludeType.Code"/>.</para>
 /// </param>
 /// <param name="cache">
 /// <para>The value specifying whether to cache the <see cref="FlexiIncludeBlock"/>'s content on disk.</para>
 /// <para>If this value is true and the <see cref="FlexiIncludeBlock"/>'s source is remote, the source's content is cached on disk.</para>
 /// <para>Caching-on-disk slows down the first markdown-to-HTML run on a system, but significantly speeds up subsequent runs:</para>
 /// <para>If on-disk caching is enabled and content from remote source "x" is included, on the first run, all content in "x" is retrieved from a server and
 /// cached in memory as well as on disk.</para>
 /// <para>Subsequent requests to retrieve content from "x" during the same run will retrieve content from the in-memory cache.</para>
 /// <para>At the end of the first run, the in-memory cache is discarded when the process dies.</para>
 /// <para>For subsequent runs on the system, if content from "x" is included again, all content from "x" is retrieved from the on-disk cache, avoiding
 /// round trips to a server.</para>
 /// <para>If you are only going to execute one run on a system (e.g when doing CI/CD), the run will take less time if on-disk caching is disabled.
 /// If you are doing multiple runs on a system, on-disk caching should be enabled.</para>
 /// <para>Defaults to <c>true</c>.</para>
 /// </param>
 /// <param name="cacheDirectory">
 /// <para>The directory to cache the <see cref="FlexiIncludeBlock"/>'s content in.</para>
 /// <para>This option is only relevant if caching on disk is enabled.</para>
 /// <para>If this value is <c>null</c>, whitespace or an empty string, a folder named "ContentCache" in the application's working directory is used instead.
 /// Note that the application's working directory is what <see cref="Directory.GetCurrentDirectory"/> returns.</para>
 /// <para>Defaults to <c>null</c>.</para>
 /// </param>
 public FlexiIncludeBlockOptions(string source = "",
                                 IList <Clipping> clippings = default,
                                 FlexiIncludeType type      = FlexiIncludeType.Code,
                                 bool cache            = true,
                                 string cacheDirectory = default)
 {
     Source    = source;
     Clippings = clippings == null ? null :
                 clippings is ReadOnlyCollection <Clipping> clippingsAsReadOnlyCollection ? clippingsAsReadOnlyCollection :
                 new ReadOnlyCollection <Clipping>(clippings);
     Type           = type;
     Cache          = cache;
     CacheDirectory = cacheDirectory;
 }
Пример #4
0
 /// <summary>
 /// Creates a <see cref="FlexiIncludeBlock"/>.
 /// </summary>
 /// <param name="source">The <see cref="FlexiIncludeBlock"/>'s source.</param>
 /// <param name="clippings">The <see cref="Clipping"/>s specifying content from the source to include.</param>
 /// <param name="type">The <see cref="FlexiIncludeBlock"/>'s type.</param>
 /// <param name="cacheDirectory">The directory to cache the <see cref="FlexiIncludeBlock"/>'s content in.</param>
 /// <param name="parentFlexiIncludeBlock">The <see cref="FlexiIncludeBlock"/>'s parent <see cref="FlexiIncludeBlock"/>.</param>
 /// <param name="containingSource">The URI of the source that contains the <see cref="FlexiIncludeBlock"/>.</param>
 /// <param name="blockParser">The <see cref="BlockParser"/> parsing the <see cref="FlexiIncludeBlock"/>.</param>
 public FlexiIncludeBlock(Uri source,
                          ReadOnlyCollection <Clipping> clippings,
                          FlexiIncludeType type,
                          string cacheDirectory,
                          FlexiIncludeBlock parentFlexiIncludeBlock,
                          string containingSource,
                          BlockParser blockParser) : base(blockParser)
 {
     Source                  = source;
     Clippings               = clippings;
     Type                    = type;
     CacheDirectory          = cacheDirectory;
     ParentFlexiIncludeBlock = parentFlexiIncludeBlock;
     ContainingSource        = containingSource;
     Children                = new List <FlexiIncludeBlock>();
 }
Пример #5
0
        /// <summary>
        /// Creates a <see cref="FlexiIncludeBlock"/>.
        /// </summary>
        /// <param name="proxyJsonBlock">The <see cref="ProxyJsonBlock"/> containing data for the <see cref="FlexiIncludeBlock"/>.</param>
        /// <param name="blockProcessor">The <see cref="BlockProcessor"/> processing the <see cref="FlexiIncludeBlock"/>.</param>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="proxyJsonBlock"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="blockProcessor"/> is <c>null</c>.</exception>
        /// <exception cref="OptionsException">Thrown if an option is invalid.</exception>
        /// <exception cref="InvalidOperationException">Thrown if a <see cref="FlexiIncludeBlock"/> cycle is found.</exception>
        /// <exception cref="BlockException">Thrown if an exception is thrown while processing the <see cref="FlexiIncludeBlock"/>'s included content.</exception>
        public FlexiIncludeBlock Create(ProxyJsonBlock proxyJsonBlock, BlockProcessor blockProcessor)
        {
            (IFlexiIncludeBlockOptions flexiIncludeBlockOptions, IFlexiIncludeBlocksExtensionOptions flexiIncludeBlocksExtensionOptions) = _optionsService.
                                                                                                                                           CreateOptions(blockProcessor, proxyJsonBlock);

            // Source
            string source = flexiIncludeBlockOptions.Source;

            ValidateSource(source);

            // Type
            FlexiIncludeType type = flexiIncludeBlockOptions.Type;

            ValidateType(type);

            // Cache directory
            string cacheDirectory = ResolveAndValidateCacheDirectory(flexiIncludeBlockOptions.Cache, flexiIncludeBlockOptions.CacheDirectory);

            // Parent
            FlexiIncludeBlock parent = ResolveParent(blockProcessor);

            // Containing source
            string containingSource = ResolveContainingSource(parent);

            // Source absolute URI
            Uri sourceAbsoluteUri = ResolveSourceAbsoluteUri(source, flexiIncludeBlocksExtensionOptions.BaseUri, parent);

            // Create block
            var flexiIncludeBlock = new FlexiIncludeBlock(sourceAbsoluteUri,
                                                          flexiIncludeBlockOptions.Clippings,
                                                          type,
                                                          cacheDirectory,
                                                          parent,
                                                          containingSource,
                                                          proxyJsonBlock.Parser)
            {
                Column = proxyJsonBlock.Column,
                Line   = proxyJsonBlock.Line,
                Span   = proxyJsonBlock.Span
            };

            parent?.Children.Add(flexiIncludeBlock);

            ProcessFlexiIncludeBlock(flexiIncludeBlock, proxyJsonBlock, blockProcessor);

            return(null);
        }