Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/includes.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ The path syntax uses forward slashes `/`.
snippet: http://myurl
</pre>

## Including a snippet from an external URL

To include a specific named snippet from a file using an external URL, use the `web-snippet:` keyword followed by the URL and the snippet key separated by a `#`:

<pre>
web-snippet&#58;https://raw.githubusercontent.com/owner/repo/branch/path/to/file.cs#snippetKey
</pre>

This will fetch the file from the URL, extract the snippet with the given key, and embed it in your Markdown.


## Markdown includes

Expand Down
10 changes: 10 additions & 0 deletions docs/mdsource/includes.source.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ The path syntax uses forward slashes `/`.
snippet&#58; http://myurl
</pre>

## Including a snippet from an external URL

To include a specific named snippet from a file using an external URL, use the `web-snippet:` keyword followed by the URL and the snippet key separated by a `#`:

<pre>
web-snippet&#58;https://raw.githubusercontent.com/owner/repo/branch/path/to/file.cs#snippetKey
</pre>

This will fetch the file from the URL, extract the snippet with the given key, and embed it in your Markdown.


## Markdown includes

Expand Down
13 changes: 13 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,19 @@ Will render:

Files are downloaded to `%temp%MarkdownSnippets` with a maximum of 100 files kept.

You can also use `web-snippet:` to reference remote content where you want to target a specific snippet defined in that content.

web-snippet: https://raw.githubusercontent.com/SimonCropp/MarkdownSnippets/main/src/Tests/DirectorySnippetExtractor/Case/code1.txt#snipPet

Will render:

<!-- web-snippet: https://raw.githubusercontent.com/SimonCropp/MarkdownSnippets/main/src/Tests/DirectorySnippetExtractor/Case/code1.txt#snipPet -->
<a id='snippet-snipPet'></a>
```txt
Some code
```
<sup><a href='#snippet-snipPet' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

### Including a full file

Expand Down
13 changes: 13 additions & 0 deletions readme.source.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,19 @@ Will render:

Files are downloaded to `%temp%MarkdownSnippets` with a maximum of 100 files kept.

You can also use `web-snippet:` to reference remote content where you want to target a specific snippet defined in that content.

web-snippet: https://raw.githubusercontent.com/SimonCropp/MarkdownSnippets/main/src/Tests/DirectorySnippetExtractor/Case/code1.txt#snipPet

Will render:

<!-- web-snippet: https://raw.githubusercontent.com/SimonCropp/MarkdownSnippets/main/src/Tests/DirectorySnippetExtractor/Case/code1.txt#snipPet -->
<a id='snippet-snipPet'></a>
```txt
Some code
```
<sup><a href='#snippet-snipPet' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

### Including a full file

Expand Down
48 changes: 48 additions & 0 deletions src/MarkdownSnippets/Processing/MarkdownProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,26 @@ void AppendSnippet(string key1)
line.Current = builder.ToString();
}

void AppendWebSnippet(string url, string snippetKey)
{
builder.Clear();
ProcessWebSnippetLine(AppendLine, missingSnippets, usedSnippets, url, snippetKey, line);
builder.TrimEnd();
line.Current = builder.ToString();
}

if (SnippetKey.ExtractSnippet(line, out var key))
{
AppendSnippet(key);
continue;
}

if (SnippetKey.ExtractWebSnippet(line, out var url, out var snippetKey))
{
AppendWebSnippet(url, snippetKey);
continue;
}

if (convention == DocumentConvention.SourceTransform)
{
continue;
Expand Down Expand Up @@ -275,6 +289,40 @@ void ProcessSnippetLine(Action<string> appendLine, List<MissingSnippet> missings
appendLine("<!-- endSnippet -->");
}

void ProcessWebSnippetLine(Action<string> appendLine, List<MissingSnippet> missings, List<Snippet> used, string url, string snippetKey, Line line)
{
appendLine($"<!-- web-snippet: {url}#{snippetKey} -->");
// Download file content
var (success, content) = Downloader.DownloadContent(url).GetAwaiter().GetResult();
if (!success || string.IsNullOrWhiteSpace(content))
{
var missing = new MissingSnippet($"{url}#{snippetKey}", line.LineNumber, line.Path);
missings.Add(missing);
appendLine("```");
appendLine($"** Could not fetch or parse web-snippet '{url}#{snippetKey}' **");
appendLine("```");
appendLine("<!-- endSnippet -->");
return;
}
// Extract snippets from content
using var reader = new StringReader(content);
var snippets = FileSnippetExtractor.Read(reader, url);
var found = snippets.FirstOrDefault(s => s.Key == snippetKey);
if (found == null)
{
var missing = new MissingSnippet($"{url}#{snippetKey}", line.LineNumber, line.Path);
missings.Add(missing);
appendLine("```");
appendLine($"** Could not find snippet '{snippetKey}' in '{url}' **");
appendLine("```");
appendLine("<!-- endSnippet -->");
return;
}
appendSnippets(snippetKey, [found], appendLine);
appendLine("<!-- endSnippet -->");
used.Add(found);
}

bool TryGetSnippets(
string key,
string? relativePath,
Expand Down
25 changes: 25 additions & 0 deletions src/MarkdownSnippets/Processing/SnippetKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,34 @@ public static bool ExtractSnippet(Line line, [NotNullWhen(true)] out string? key
return true;
}

public static bool ExtractWebSnippet(Line line, [NotNullWhen(true)] out string? url, [NotNullWhen(true)] out string? snippetKey)
{
var lineCurrent = line.Current;
if (!IsWebSnippetLine(lineCurrent))
{
url = null;
snippetKey = null;
return false;
}
var value = lineCurrent[12..].Trim(); // after 'web-snippet:', fixed from 11 to 12
var hashIndex = value.LastIndexOf('#');
if (hashIndex < 0 || hashIndex == value.Length - 1)
{
url = null;
snippetKey = null;
return false;
}
url = value[..hashIndex];
snippetKey = value[(hashIndex + 1)..];
return true;
}

public static bool IsSnippetLine(string line) =>
line.StartsWith("snippet:", StringComparison.OrdinalIgnoreCase);

public static bool IsStartCommentSnippetLine(string line) =>
line.StartsWith("<!-- snippet:", StringComparison.OrdinalIgnoreCase);

public static bool IsWebSnippetLine(string line) =>
line.StartsWith("web-snippet:", StringComparison.OrdinalIgnoreCase);
}
18 changes: 18 additions & 0 deletions src/Tests/WebSnippetTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
public class WebSnippetTests
{
[Fact]
public void ExtractWebSnippet_ParsesCorrectly()
{
var line = new Line("web-snippet:https://example.com/file.cs#mysnippet", "", 1);
Assert.True(SnippetKey.ExtractWebSnippet(line, out var url, out var key));
Assert.Equal("https://example.com/file.cs", url);
Assert.Equal("mysnippet", key);
}

[Fact]
public void ExtractWebSnippet_FailsWithoutHash()
{
var line = new Line("web-snippet:https://example.com/file.cs", "", 1);
Assert.False(SnippetKey.ExtractWebSnippet(line, out var _, out var _));
}
}