diff --git a/readme.md b/readme.md
index b391cf15..437931b8 100644
--- a/readme.md
+++ b/readme.md
@@ -230,6 +230,32 @@ Will render:
anchor
+### Including a partial file
+
+If no snippet is found matching the defined name. The target directory will be searched for a file matching that name.
+Using a syntax of #L10-20 you can only include lines 10 to 20 from that file. For example:
+
+
+
+ ```txt
+ The MIT License (MIT)
+ ...
+ ```
+ anchor
+
+
+### Including a C# Method
+
+If no snippet is found matching the defined name. The target directory will be searched for a file matching that name.
+Using a syntax of #M-MethodName you can reference a specific method. For example:
+
+
+
+ ```cs
+ ...
+ ```
+ anchor
+
### LinkFormat
diff --git a/readme.source.md b/readme.source.md
index 03d14000..aba32738 100644
--- a/readme.source.md
+++ b/readme.source.md
@@ -223,6 +223,32 @@ Will render:
anchor
+### Including a partial file
+
+If no snippet is found matching the defined name. The target directory will be searched for a file matching that name.
+Using a syntax of #L10-20 you can only include lines 10 to 20 from that file. For example:
+
+
+
+ ```txt
+ The MIT License (MIT)
+ ...
+ ```
+ anchor
+
+
+### Including a C# Method
+
+If no snippet is found matching the defined name. The target directory will be searched for a file matching that name.
+Using a syntax of #M-MethodName you can reference a specific method. For example:
+
+
+
+ ```cs
+ ...
+ ```
+ anchor
+
### LinkFormat
diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index 394bb0ed..ae9fad6c 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -7,6 +7,7 @@
+
diff --git a/src/MarkdownSnippets/GlobalUsings.cs b/src/MarkdownSnippets/GlobalUsings.cs
index 42f8fa5e..eabc4967 100644
--- a/src/MarkdownSnippets/GlobalUsings.cs
+++ b/src/MarkdownSnippets/GlobalUsings.cs
@@ -1,5 +1,10 @@
-global using System.Diagnostics.CodeAnalysis;
+global using System.Diagnostics.CodeAnalysis;
global using System.Net;
global using System.Net.Http;
global using System.Text.RegularExpressions;
-global using MarkdownSnippets;
\ No newline at end of file
+global using MarkdownSnippets;
+
+global using System.Text;
+global using Microsoft.CodeAnalysis;
+global using Microsoft.CodeAnalysis.CSharp;
+global using Microsoft.CodeAnalysis.CSharp.Syntax;
\ No newline at end of file
diff --git a/src/MarkdownSnippets/MarkdownSnippets.csproj b/src/MarkdownSnippets/MarkdownSnippets.csproj
index c009ddf8..e46c4de7 100644
--- a/src/MarkdownSnippets/MarkdownSnippets.csproj
+++ b/src/MarkdownSnippets/MarkdownSnippets.csproj
@@ -1,9 +1,9 @@
-
netstandard2.0;net48;net8.0
+
diff --git a/src/MarkdownSnippets/Processing/MarkdownProcessor.cs b/src/MarkdownSnippets/Processing/MarkdownProcessor.cs
index 01c606ca..e449da96 100644
--- a/src/MarkdownSnippets/Processing/MarkdownProcessor.cs
+++ b/src/MarkdownSnippets/Processing/MarkdownProcessor.cs
@@ -311,9 +311,18 @@ bool FilesToSnippets(
return true;
}
+ string? lineExpression = null;
+
+ if (key.Contains('#'))
+ {
+ var splitByHash = key.Split('#', StringSplitOptions.None);
+ key = splitByHash[0];
+ lineExpression = splitByHash[1];
+ }
+
if (RelativeFile.Find(allFiles, targetDirectory, key, relativePath, linePath, out var path))
{
- snippetsForKey = SnippetsForFile(key, path);
+ snippetsForKey = SnippetsForFile(key, path, lineExpression);
return true;
}
@@ -322,8 +331,7 @@ bool FilesToSnippets(
}
- List SnippetsForFile(string key, string relativeToRoot) =>
- [FileToSnippet(key, relativeToRoot, null)];
+ List SnippetsForFile(string key, string relativeToRoot, string? lines = null) => [FileToSnippet(key, relativeToRoot, null, lines)];
bool GetForHttp(string key, out IReadOnlyList snippetsForKey)
{
@@ -338,22 +346,77 @@ bool GetForHttp(string key, out IReadOnlyList snippetsForKey)
return true;
}
- Snippet FileToSnippet(string key, string file, string? path)
+ Snippet FileToSnippet(string key, string file, string? path, string? linesExpression = null)
+ {
+ var language = Path.GetExtension(file)[1..];
+
+ if (linesExpression == null)
+ {
+ var (text, endLine) = ReadNonStartEndLines(file);
+ return Snippet.Build(startLine: 1, endLine: endLine, value: text, key: key, language: language, path: path);
+ }
+
+ if (linesExpression[0] == 'L')
+ {
+ return ParseSpecificLines(key, file, path, linesExpression, language);
+ }
+
+ if (linesExpression[0] == 'M' && language == "cs")
+ {
+ return ParseCSharpMethod(key, file, path, linesExpression, language);
+ }
+
+ throw new SnippetException($"Unable to parse expression '{linesExpression}'");
+ }
+
+ static Snippet ParseCSharpMethod(string key, string file, string? path, string linesExpression, string language)
{
- var (text, lineCount) = ReadNonStartEndLines(file);
+ var methodName = linesExpression[2..];
+
+ var code = File.ReadAllText(file);
+ var tree = CSharpSyntaxTree.ParseText(code);
+ var root = tree.GetCompilationUnitRoot();
+
+ var method = root.DescendantNodes()
+ .OfType()
+ .FirstOrDefault(m => m.Identifier.Text == methodName);
+
+ if (method != null)
+ {
+ var text = method.ToFullString();
+ var startLine = method.Span.Start;
+ var endLine = method.Span.End;
+
+ return Snippet.Build(startLine: startLine, endLine: endLine, value: text, key: key, language: language, path: path);
+ }
+
+ throw new SnippetException(
+ $"""
+ Failed to find method {methodName} in file {file} configuration.
+ Content:
+ {code}
+ """);
+ }
+
+ static Snippet ParseSpecificLines(string key, string file, string? path, string linesExpression, string language)
+ {
+ var text = string.Empty;
+
+ var expressionSplit = linesExpression.Split('-');
+ var startLine = int.Parse(expressionSplit[0][1..]);
+ var endLine = int.Parse(expressionSplit[1]);
+
+ var fileLines = File.ReadAllLines(file);
+
+ var selectedText = new StringBuilder();
- if (lineCount == 0)
+ for (var index = startLine; index < endLine; index++)
{
- lineCount++;
+ selectedText.AppendLine(fileLines[index]);
+ text = selectedText.ToString();
}
- return Snippet.Build(
- startLine: 1,
- endLine: lineCount,
- value: text,
- key: key,
- language: Path.GetExtension(file)[1..],
- path: path);
+ return Snippet.Build(startLine: startLine, endLine: endLine, value: text, key: key, language: language, path: path);
}
(string text, int lineCount) ReadNonStartEndLines(string file)