Extract code snippets from any language to be used when building documentation
Loosely based on some code from https://github.com/shiftkey/scribble
This repository previously contained a two variants. Both a complex (CaptureSnippets), and simplified (CaptureSnippetsSimple) variants. CaptureSnippetsSimple has since been moved and renamed to MarkdownSnippets.
The keyed snippets can then be used in any documentation .md
file by adding the text snippet: KEY
.
Then snippets with the key (all versions) will be rendered in a tabbed manner. If there is only a single version then it will be rendered as a simple code block with no tabs.
For example
Some blurb about the below snippet snippet: MySnippetName
The resulting markdown will be will be:
Some blurb about the below snippet
```
My Snippet Code
```
Any code wrapped in a convention based comment will be picked up. The comment needs to start with startcode
which is followed by the key. The snippet is then terminated by endcode
.
// startcode MySnippetName
My Snippet Code
// endcode
Any code wrapped in a named C# region will be picked up. The name of the region is used as the key.
#region MySnippetName
My Snippet Code
#endregion
The code snippets will do smart trimming of snippet indentation.
For example given this snippet:
••#region MySnippetName ••Line one of the snippet ••••Line two of the snippet ••#endregion
The leading two spaces (••) will be trimmed and the result will be:
Line one of the snippet
••Line two of the snippet
The same behavior will apply to leading tabs.
If tabs and spaces are mixed there is no way for the snippets to work out what to trim.
So given this snippet:
••#region MySnippetNamea ••Line one of the snippet ➙➙Line one of the snippet ••#endregion
Where ➙ is a tab.
The resulting markdown will be will be
Line one of the snippet ➙➙Line one of the snippet
Note none of the tabs have been trimmed.
When scanning for snippets the following are ignored:
- All directories and files starting with a period
.
- All binary files as defined by https://github.com/sindresorhus/binary-extensions/
- Any of the following directory names:
bin
,obj
To change these conventions manipulate lists CaptureSnippets.Exclusions.ExcludedDirectorySuffixes
and CaptureSnippets.Exclusions.ExcludedFileExtensions
.
https://nuget.org/packages/CaptureSnippets/
PM> Install-Package CaptureSnippets
Component -> Package -> VersionGroup -> Snippets
The requirement for Component and Package is required due to Package being strongly tied to a deployment concept, where Component allows a logical concept. This enabled several scenarios:
- Packages to be renamed, but still tied to a single parent Component from a functionality perspective.
- Multiple Packages, that represent different parts of that same logical feature, to be grouped under a Component.
The directory convention follows the below structure:
Root Dir -> Component Dirs -> Package Version Dirs -> Snippet Files
A Shared
directory can exist at the Root and/or Component levels that contains snippet files that are shared either globally or for a Component respectively.
So an example directory structure could be as follows:
- ComponentX
- PackageA_1
- PackageA_2
- PackageB_1
- Shared
- ComponentY
- PackageC_1
- PackageC_2
- Shared
Snippets are versioned.
Version follows the NuGet version range syntax.
For more details on NuGet versioning see https://www.nuget.org/packages/NuGet.Versioning/ and https://github.com/NuGet/NuGet.Client.
Package directories can be suffixed with a version delimited by an underscore _
. For example PackageA_2
contains all snippets for PackageA
version 2. Version ranges are not supported as package suffixes.
Appending a version to the end of a snippet definition as follows.
#region MySnippetName 4.5
My Snippet Code
#endregion
Or a version range
#region MySnippetName [1.0,2.0]
My Snippet Code
#endregion
var files = Directory.EnumerateFiles(@"C:\path", "*.cs", SearchOption.AllDirectories);
var snippetExtractor = FileSnippetExtractor.Build(
fileVersion: VersionRange.Parse("[1.1,2.0)"),
package: "ThePackageName",
isCurrent: true);
var snippets = snippetExtractor.Read(files);
IEnumerable<string> PackageOrder(string component)
{
if (component == "component1")
{
return new List<string>
{
"package1",
"package2"
};
}
return Enumerable.Empty<string>();
}
string TranslatePackage(string packageAlias)
{
if (packageAlias == "shortName")
{
return "theFullPackageName";
}
return packageAlias;
}
// setup version convention and extract snippets from files
var snippetExtractor = new DirectorySnippetExtractor(
// all directories except bin and obj
directoryFilter: dirPath => !dirPath.EndsWith("bin") && !dirPath.EndsWith("obj"),
// all vm and cs files
fileFilter: filePath => filePath.EndsWith(".vm") || filePath.EndsWith(".cs"),
// package order is optional
packageOrder: PackageOrder,
// package translation is optional
translatePackage: TranslatePackage
);
var components = snippetExtractor.ReadComponents(@"C:\path");
var component1 = components.GetComponent("Component1");
var packagesForComponent1 = component1.Packages;
var snippetsForComponent1 = component1.Snippets;
var packages = snippetExtractor.ReadPackages(@"C:\path");
var package1 = components.GetComponent("Package1");
var snippetsForPackage1 = package1.Snippets;
// The below snippets could also be accessed via
// * packages.Snippets
// * components.AllSnippets
var snippets = snippetExtractor.ReadSnippets(@"C:\path");
// setup version convention and extract snippets from files
var snippetExtractor = new DirectorySnippetExtractor(
directoryFilter: x => true,
fileFilter: s => s.EndsWith(".vm") || s.EndsWith(".cs"));
var snippets = snippetExtractor.ReadSnippets(@"C:\path");
// Merge with some markdown text
var markdownProcessor = new MarkdownProcessor(snippets, SimpleSnippetMarkdownHandling.AppendGroup);
using (var reader = File.OpenText(@"C:\path\inputMarkdownFile.md"))
using (var writer = File.CreateText(@"C:\path\outputMarkdownFile.md"))
{
var result = markdownProcessor.Apply(reader, writer);
// snippets that the markdown file expected but did not exist in the input snippets
var missingSnippets = result.MissingSnippets;
// snippets that the markdown file used
var usedSnippets = result.UsedSnippets;
}
Icon courtesy of The Noun Project and is licensed under Creative Commons Attribution as:
"Net" by Stanislav Cherenkov from The Noun Project