Skip to content

Commit 09bf07a

Browse files
committed
optimize leftover examples
1 parent 64780f1 commit 09bf07a

18 files changed

Lines changed: 1739 additions & 311 deletions
Lines changed: 4 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,13 @@
11
using System;
2-
using System.Collections.Generic;
32
using System.IO;
43
using System.Linq;
54
using System.Text;
6-
using System.Text.RegularExpressions;
7-
using System.Threading.Tasks;
5+
using Blazorise.Docs.Compiler.ExampleSources;
86

97
namespace Blazorise.Docs.Compiler;
108

119
public class CodeSnippets
1210
{
13-
private const string CopyPasteReadyMarker = "@* copy-paste-ready *@";
14-
private const string DataGridPackageUsingPattern = "(?m)^@using Blazorise(?:[.]DataGrid|[.]Shared[.](?:Data|Models))?\\r?\\n";
15-
private const string EmployeeDataInjectionPattern = @"(?m)^(?<indent>[ \t]*)\[Inject\][ \t]+EmployeeData EmployeeData \{ get; set; \}";
16-
private const string RazorDocsDirectivePattern = "@(namespace|layout|page) .+?\\r?\\n";
17-
18-
private static readonly string[] DataGridSupportFiles =
19-
[
20-
"DataGridEmployeeData.csharp",
21-
"DataGridEmployee.csharp",
22-
"DataGridSalary.csharp"
23-
];
24-
25-
private static readonly string[] DataGridRequiredUsings =
26-
[
27-
"@using System",
28-
"@using System.Collections.Generic",
29-
"@using System.ComponentModel.DataAnnotations",
30-
"@using System.Linq",
31-
"@using System.Threading.Tasks"
32-
];
33-
3411
public bool Execute()
3512
{
3613
var success = true;
@@ -94,132 +71,13 @@ private static string EscapeComponentSource( string path )
9471
{
9572
var source = File.ReadAllText( path, Encoding.UTF8 );
9673
source = PrepareSourceForCopy( path, source );
97-
source = Regex.Replace( source, RazorDocsDirectivePattern, string.Empty );
74+
source = ExampleSourceComposerHelpers.RemoveDocsDirectives( source );
9875
return source.Replace( "\"", "\"\"" ).Trim().ToCrLfLineEndings();
9976
}
10077

10178
internal static string PrepareSourceForDisplay( string path, string source )
102-
=> PrepareSource( path, source, false );
79+
=> ExampleSourceComposerPipeline.PrepareForDisplay( path, source );
10380

10481
internal static string PrepareSourceForCopy( string path, string source )
105-
=> PrepareSource( path, source, true );
106-
107-
private static string PrepareSource( string path, string source, bool composeFullExample )
108-
{
109-
bool isDataGridExample = path.Replace( '\\', '/' ).Contains( "/Extensions/DataGrid/Examples/", StringComparison.Ordinal );
110-
111-
if ( isDataGridExample )
112-
{
113-
ValidateDataGridSource( path, source );
114-
115-
if ( Path.GetExtension( path ).Equals( ".razor", StringComparison.OrdinalIgnoreCase ) )
116-
{
117-
source = Regex.Replace(
118-
source,
119-
EmployeeDataInjectionPattern,
120-
"${indent}private readonly EmployeeData EmployeeData = new();" );
121-
122-
if ( composeFullExample && !source.Contains( CopyPasteReadyMarker, StringComparison.Ordinal ) )
123-
{
124-
source = InlineDataGridSupportTypes( path, source );
125-
}
126-
127-
source = Regex.Replace( source, DataGridPackageUsingPattern, string.Empty );
128-
}
129-
else if ( !composeFullExample )
130-
{
131-
source = Regex.Replace( source, "(?m)^using Blazorise[.]Shared[.](?:Data|Models);\\r?\\n", string.Empty );
132-
source = Regex.Replace( source, "(?m)^namespace Blazorise[.]Shared[.](?:Data|Models);\\r?\\n", string.Empty );
133-
}
134-
135-
source = Regex.Replace( source, RazorDocsDirectivePattern, string.Empty );
136-
}
137-
138-
if ( source.Contains( CopyPasteReadyMarker, StringComparison.Ordinal ) )
139-
{
140-
ValidateCopyPasteReadySource( path, source );
141-
source = Regex.Replace( source, $"{Regex.Escape( CopyPasteReadyMarker )}\\r?\\n", string.Empty );
142-
}
143-
144-
return source;
145-
}
146-
147-
private static string InlineDataGridSupportTypes( string path, string source )
148-
{
149-
string[] missingUsings = DataGridRequiredUsings
150-
.Where( requiredUsing => !Regex.IsMatch( source, $"(?m)^{Regex.Escape( requiredUsing )}\\r?$" ) )
151-
.ToArray();
152-
153-
if ( missingUsings.Length > 0 )
154-
{
155-
Match namespaceDirective = Regex.Match( source, "@namespace[^\\r\\n]+\\r?\\n" );
156-
157-
if ( !namespaceDirective.Success )
158-
throw new InvalidOperationException( $"DataGrid example '{path}' must declare a namespace before its copy-ready imports can be composed." );
159-
160-
string imports = string.Join( Environment.NewLine, missingUsings ) + Environment.NewLine;
161-
source = source.Insert( namespaceDirective.Index + namespaceDirective.Length, imports );
162-
}
163-
164-
string examplesDirectory = Path.GetDirectoryName( path );
165-
string supportTypes = string.Join(
166-
Environment.NewLine + Environment.NewLine,
167-
DataGridSupportFiles.Select( file => ExtractSupportTypes( Path.Combine( examplesDirectory, file ) ) ) );
168-
169-
int codeBlockEnd = source.LastIndexOf( '}' );
170-
171-
if ( codeBlockEnd < 0 || !source.Contains( "@code", StringComparison.Ordinal ) )
172-
throw new InvalidOperationException( $"DataGrid example '{path}' must end with an @code block so support types can be inlined." );
173-
174-
string indentedSupportTypes = string.Join(
175-
Environment.NewLine,
176-
supportTypes.Split( ["\r\n", "\n"], StringSplitOptions.None ).Select( line => $" {line}" ) );
177-
178-
return source.Insert( codeBlockEnd, $"{Environment.NewLine}{Environment.NewLine}{indentedSupportTypes}{Environment.NewLine}" );
179-
}
180-
181-
private static string ExtractSupportTypes( string path )
182-
{
183-
string source = File.ReadAllText( path, Encoding.UTF8 );
184-
source = Regex.Replace( source, "(?m)^using .+;\\r?\\n", string.Empty );
185-
source = Regex.Replace( source, "(?m)^namespace .+;\\r?\\n", string.Empty );
186-
return source.Trim();
187-
}
188-
189-
private static void ValidateCopyPasteReadySource( string path, string source )
190-
{
191-
string[] docsOnlyDependencies =
192-
[
193-
"@inject ",
194-
"[Inject]",
195-
"Blazorise.Shared",
196-
"EmployeeData"
197-
];
198-
199-
string docsOnlyDependency = docsOnlyDependencies.FirstOrDefault( dependency => source.Contains( dependency, StringComparison.Ordinal ) );
200-
201-
if ( docsOnlyDependency is not null )
202-
throw new InvalidOperationException( $"Copy-paste-ready example '{path}' references docs-only dependency '{docsOnlyDependency}'." );
203-
204-
if ( !source.Contains( "@code", StringComparison.Ordinal ) )
205-
throw new InvalidOperationException( $"Copy-paste-ready example '{path}' must include its component state in an @code block." );
206-
}
207-
208-
private static void ValidateDataGridSource( string path, string source )
209-
{
210-
string[] docsOnlyDependencies =
211-
[
212-
"GetManifestResourceStream",
213-
"IMemoryCache"
214-
];
215-
216-
string docsOnlyDependency = docsOnlyDependencies.FirstOrDefault( dependency => source.Contains( dependency, StringComparison.Ordinal ) );
217-
218-
if ( docsOnlyDependency is not null )
219-
throw new InvalidOperationException( $"DataGrid example source '{path}' references docs-only dependency '{docsOnlyDependency}'." );
220-
221-
if ( source.Contains( "new EmployeeData", StringComparison.Ordinal ) )
222-
throw new InvalidOperationException( $"DataGrid example source '{path}' must inject the runtime EmployeeData service. The docs compiler makes the displayed source self-contained." );
223-
224-
}
82+
=> ExampleSourceComposerPipeline.PrepareForCopy( path, source );
22583
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
using System;
2+
using System.IO;
3+
using System.Text.RegularExpressions;
4+
using static Blazorise.Docs.Compiler.ExampleSources.ExampleSourceComposerHelpers;
5+
6+
namespace Blazorise.Docs.Compiler.ExampleSources;
7+
8+
internal sealed class CountryExampleSourceComposer : IExampleSourceComposer
9+
{
10+
private const string CountryDataInjectionPattern = @"(?m)^(?<indent>[ \t]*)\[Inject\]\r?\n[ \t]*public CountryData CountryData \{ get; set; \}";
11+
12+
private const string PortableCountryDataSource = """
13+
using System.Collections.Generic;
14+
using System.Threading.Tasks;
15+
16+
public class CountryData
17+
{
18+
public Task<IEnumerable<Country>> GetDataAsync()
19+
=> Task.FromResult<IEnumerable<Country>>( new Country[]
20+
{
21+
new( "Croatia", "HR", "Zagreb" ),
22+
new( "France", "FR", "Paris" ),
23+
new( "Germany", "DE", "Berlin" ),
24+
new( "Italy", "IT", "Rome" ),
25+
new( "Japan", "JP", "Tokyo" ),
26+
new( "Portugal", "PT", "Lisbon" ),
27+
new( "Spain", "ES", "Madrid" ),
28+
new( "United Kingdom", "GB", "London" ),
29+
new( "United States", "US", "Washington, D.C." )
30+
} );
31+
}
32+
""";
33+
34+
private static readonly string[] RequiredUsings =
35+
[
36+
"@using System",
37+
"@using System.Collections.Generic",
38+
"@using System.ComponentModel.DataAnnotations",
39+
"@using System.Linq",
40+
"@using System.Threading.Tasks"
41+
];
42+
43+
public bool CanHandle( string normalizedPath )
44+
=> normalizedPath.Contains( "/Extensions/Autocomplete/Examples/", StringComparison.Ordinal )
45+
|| normalizedPath.Contains( "/Extensions/DropdownList/Examples/", StringComparison.Ordinal )
46+
|| normalizedPath.Contains( "/Extensions/ListView/Examples/", StringComparison.Ordinal );
47+
48+
public string Prepare( string path, string source, ExampleSourceMode mode )
49+
{
50+
string filename = Path.GetFileName( path );
51+
52+
if ( filename.Equals( "CountryData.csharp", StringComparison.OrdinalIgnoreCase ) )
53+
return PortableCountryDataSource;
54+
55+
if ( filename.Equals( "Country.csharp", StringComparison.OrdinalIgnoreCase ) )
56+
return Regex.Replace( source, "(?m)^namespace .+;\\r?\\n", string.Empty );
57+
58+
if ( !Path.GetExtension( path ).Equals( ".razor", StringComparison.OrdinalIgnoreCase )
59+
|| !source.Contains( "CountryData", StringComparison.Ordinal ) )
60+
return source;
61+
62+
source = Regex.Replace(
63+
source,
64+
CountryDataInjectionPattern,
65+
"${indent}private readonly CountryData CountryData = new();" );
66+
67+
if ( mode == ExampleSourceMode.Copy )
68+
{
69+
source = AddRequiredUsings( path, source, RequiredUsings );
70+
71+
string supportTypes = string.Join(
72+
Environment.NewLine + Environment.NewLine,
73+
ExtractSupportTypesFromSource( PortableCountryDataSource ),
74+
ExtractSupportTypes( SupportPath( path, "Country.csharp" ) ) );
75+
76+
source = AppendToCodeBlock( path, source, supportTypes );
77+
ValidateComposedSource( path, source, ["Blazorise.Shared", "IMemoryCache", "GetManifestResourceStream", "[Inject]"] );
78+
}
79+
80+
return source;
81+
}
82+
83+
private static string SupportPath( string path, string filename )
84+
{
85+
string examplesDirectory = Path.GetDirectoryName( path );
86+
string supportPath = Path.Combine( examplesDirectory, filename );
87+
88+
if ( File.Exists( supportPath ) )
89+
return supportPath;
90+
91+
string extensionsDirectory = Path.GetFullPath( Path.Combine( examplesDirectory, "..", ".." ) );
92+
supportPath = Path.Combine( extensionsDirectory, "Autocomplete", "Examples", filename );
93+
94+
if ( !File.Exists( supportPath ) )
95+
throw new InvalidOperationException( $"Country example '{path}' is missing support file '{filename}'." );
96+
97+
return supportPath;
98+
}
99+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
using System;
2+
using System.IO;
3+
using System.Linq;
4+
using System.Text.RegularExpressions;
5+
using static Blazorise.Docs.Compiler.ExampleSources.ExampleSourceComposerHelpers;
6+
7+
namespace Blazorise.Docs.Compiler.ExampleSources;
8+
9+
internal sealed class DataGridExampleSourceComposer : IExampleSourceComposer
10+
{
11+
private const string PackageUsingPattern = "(?m)^@using Blazorise(?:[.]DataGrid|[.]Shared[.](?:Data|Models))?\\r?\\n";
12+
private const string EmployeeDataInjectionPattern = @"(?m)^(?<indent>[ \t]*)\[Inject\][ \t]+EmployeeData EmployeeData \{ get; set; \}";
13+
14+
private static readonly string[] SupportFiles =
15+
[
16+
"DataGridEmployeeData.csharp",
17+
"DataGridEmployee.csharp",
18+
"DataGridSalary.csharp"
19+
];
20+
21+
private static readonly string[] RequiredUsings =
22+
[
23+
"@using System",
24+
"@using System.Collections.Generic",
25+
"@using System.ComponentModel.DataAnnotations",
26+
"@using System.Linq",
27+
"@using System.Threading.Tasks"
28+
];
29+
30+
public bool CanHandle( string normalizedPath )
31+
=> normalizedPath.Contains( "/Extensions/DataGrid/Examples/", StringComparison.Ordinal );
32+
33+
public string Prepare( string path, string source, ExampleSourceMode mode )
34+
{
35+
ValidateSource( path, source );
36+
37+
if ( Path.GetExtension( path ).Equals( ".razor", StringComparison.OrdinalIgnoreCase ) )
38+
{
39+
source = Regex.Replace(
40+
source,
41+
EmployeeDataInjectionPattern,
42+
"${indent}private readonly EmployeeData EmployeeData = new();" );
43+
44+
if ( mode == ExampleSourceMode.Copy && !source.Contains( CopyPasteReadyMarker, StringComparison.Ordinal ) )
45+
{
46+
source = InlineSupportTypes( path, source );
47+
}
48+
49+
source = Regex.Replace( source, PackageUsingPattern, string.Empty );
50+
}
51+
else if ( mode == ExampleSourceMode.Display )
52+
{
53+
source = Regex.Replace( source, "(?m)^using Blazorise[.]Shared[.](?:Data|Models);\\r?\\n", string.Empty );
54+
source = Regex.Replace( source, "(?m)^namespace Blazorise[.]Shared[.](?:Data|Models);\\r?\\n", string.Empty );
55+
}
56+
57+
return RemoveDocsDirectives( source );
58+
}
59+
60+
private static string InlineSupportTypes( string path, string source )
61+
{
62+
source = AddRequiredUsings( path, source, RequiredUsings );
63+
64+
string examplesDirectory = Path.GetDirectoryName( path );
65+
string supportTypes = string.Join(
66+
Environment.NewLine + Environment.NewLine,
67+
SupportFiles.Select( file => ExtractSupportTypes( Path.Combine( examplesDirectory, file ) ) ) );
68+
69+
return AppendToCodeBlock( path, source, supportTypes );
70+
}
71+
72+
private static void ValidateSource( string path, string source )
73+
{
74+
string[] docsOnlyDependencies =
75+
[
76+
"GetManifestResourceStream",
77+
"IMemoryCache"
78+
];
79+
80+
string docsOnlyDependency = docsOnlyDependencies.FirstOrDefault( dependency => source.Contains( dependency, StringComparison.Ordinal ) );
81+
82+
if ( docsOnlyDependency is not null )
83+
throw new InvalidOperationException( $"DataGrid example source '{path}' references docs-only dependency '{docsOnlyDependency}'." );
84+
85+
if ( source.Contains( "new EmployeeData", StringComparison.Ordinal ) )
86+
throw new InvalidOperationException( $"DataGrid example source '{path}' must inject the runtime EmployeeData service. The docs compiler makes the displayed source self-contained." );
87+
}
88+
}

0 commit comments

Comments
 (0)