Skip to content

Commit 7e801ad

Browse files
authored
Merge pull request #2 from aglasencnik/dev
Merge dev into main
2 parents 0fc459b + b6d878b commit 7e801ad

18 files changed

+842
-23
lines changed

BlazoryVS/BlazoryVS.csproj

+36-1
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,49 @@
4545
<WarningLevel>4</WarningLevel>
4646
</PropertyGroup>
4747
<ItemGroup>
48+
<Compile Include="BlazoryVSDefaults.cs" />
49+
<Compile Include="Enums\SnippetType.cs" />
50+
<Compile Include="Helpers\CodeParserHelper.cs" />
51+
<Compile Include="Helpers\PathHelper.cs" />
52+
<Compile Include="Helpers\SnippetDeserializationHelper.cs" />
53+
<Compile Include="Helpers\SnippetSerializationHelper.cs" />
54+
<Compile Include="Models\JsonSnippet.cs" />
55+
<Compile Include="Models\Literal.cs" />
56+
<Compile Include="Models\Snippet.cs" />
57+
<Compile Include="Models\SnippetComparisonReport.cs" />
4858
<Compile Include="Properties\AssemblyInfo.cs" />
4959
<Compile Include="BlazoryVSPackage.cs" />
60+
<Compile Include="Services\SettingsService.cs" />
61+
<Compile Include="Services\SnippetService.cs" />
5062
</ItemGroup>
5163
<ItemGroup>
64+
<Content Include="Snippets\Blazory CSharp Snippets\placeholder.snippet">
65+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
66+
<IncludeInVSIX>true</IncludeInVSIX>
67+
</Content>
68+
<Content Include="Snippets\Blazory Razor Snippets\placeholder.snippet">
69+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
70+
<IncludeInVSIX>true</IncludeInVSIX>
71+
</Content>
5272
<None Include="source.extension.vsixmanifest">
5373
<SubType>Designer</SubType>
5474
</None>
5575
</ItemGroup>
5676
<ItemGroup>
5777
<Reference Include="System" />
78+
<Reference Include="System.Net.Http" />
79+
<Reference Include="System.Xml" />
80+
<Reference Include="System.Xml.Linq" />
5881
</ItemGroup>
5982
<ItemGroup>
60-
<PackageReference Include="Microsoft.VisualStudio.SDK" Version="17.0.32112.339" ExcludeAssets="runtime" />
83+
<PackageReference Include="Community.VisualStudio.SourceGenerators">
84+
<Version>1.0.3</Version>
85+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
86+
<PrivateAssets>all</PrivateAssets>
87+
</PackageReference>
88+
<PackageReference Include="Microsoft.VisualStudio.SDK" Version="17.7.37357" ExcludeAssets="runtime">
89+
<IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
90+
</PackageReference>
6191
<PackageReference Include="Microsoft.VSSDK.BuildTools" Version="17.7.2196" />
6292
</ItemGroup>
6393
<ItemGroup>
@@ -69,7 +99,12 @@
6999
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
70100
<IncludeInVSIX>true</IncludeInVSIX>
71101
</Content>
102+
<Content Include="Snippets\Snippets.pkgdef">
103+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
104+
<IncludeInVSIX>true</IncludeInVSIX>
105+
</Content>
72106
</ItemGroup>
107+
<ItemGroup />
73108
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
74109
<Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />
75110
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

BlazoryVS/BlazoryVSDefaults.cs

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
namespace BlazoryVS
2+
{
3+
/// <summary>
4+
/// Represents the default constant values.
5+
/// </summary>
6+
internal class BlazoryVSDefaults
7+
{
8+
#region Path constants
9+
10+
/// <summary>
11+
/// Gets the name of the folder that contains the snippets.
12+
/// </summary>
13+
public const string SnippetsFolderName = "Snippets";
14+
15+
/// <summary>
16+
/// Gets the name of the folder that contains the C# snippets.
17+
/// </summary>
18+
public const string CSharpSnippetsFolderName = "Blazory CSharp Snippets";
19+
20+
/// <summary>
21+
/// Gets the name of the folder that contains the Razor snippets.
22+
/// </summary>
23+
public const string RazorSnippetsFolderName = "Blazory Razor Snippets";
24+
25+
#endregion
26+
27+
#region Snippet constants
28+
29+
/// <summary>
30+
/// Gets the name of the snippet author.
31+
/// </summary>
32+
public const string SnippetAuthor = "Blazory";
33+
34+
/// <summary>
35+
/// Gets the name of the C# snippet language.
36+
/// </summary>
37+
public const string CSharpSnippetLanguage = "CSharp";
38+
39+
/// <summary>
40+
/// Gets the name of the Razor snippet language.
41+
/// </summary>
42+
public const string RazorSnippetLanguage = "Razor";
43+
44+
/// <summary>
45+
/// Gets the name of the placeholder snippet.
46+
/// </summary>
47+
public const string PlaceholderSnippetName = "placeholder.snippet";
48+
49+
#endregion
50+
51+
#region Blazory repository constants
52+
53+
/// <summary>
54+
/// Gets the URL of the Blazory C# snippets JSON file.
55+
/// </summary>
56+
public const string CSharpSnippetsJsonUrl = "https://raw.githubusercontent.com/bartvanhoey/Blazory/master/snippets/csharp.json";
57+
58+
/// <summary>
59+
/// Gets the URL of the Blazory Razor snippets JSON file.
60+
/// </summary>
61+
public const string RazorSnippetsJsonUrl = "https://raw.githubusercontent.com/bartvanhoey/Blazory/master/snippets/razor.json";
62+
63+
#endregion
64+
65+
#region Settings constants
66+
67+
/// <summary>
68+
/// Gets the name of the C# snippets setting.
69+
/// </summary>
70+
public const string CSharpSnippetsSettingName = "CSharpSnippets";
71+
72+
/// <summary>
73+
/// Gets the name of the Razor snippets setting.
74+
/// </summary>
75+
public const string RazorSnippetsSettingName = "RazorSnippets";
76+
77+
#endregion
78+
}
79+
}

BlazoryVS/BlazoryVSPackage.cs

+47-20
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
using Microsoft.VisualStudio.Shell;
1+
using BlazoryVS.Enums;
2+
using BlazoryVS.Services;
3+
using Microsoft.VisualStudio.Shell;
4+
using Microsoft.VisualStudio.Shell.Interop;
5+
using Microsoft.VisualStudio.Shell.Settings;
26
using System;
37
using System.Runtime.InteropServices;
48
using System.Threading;
@@ -7,28 +11,16 @@
711
namespace BlazoryVS
812
{
913
/// <summary>
10-
/// This is the class that implements the package exposed by this assembly.
14+
/// Represents the entry extension class.
1115
/// </summary>
12-
/// <remarks>
13-
/// <para>
14-
/// The minimum requirement for a class to be considered a valid package for Visual Studio
15-
/// is to implement the IVsPackage interface and register itself with the shell.
16-
/// This package uses the helper classes defined inside the Managed Package Framework (MPF)
17-
/// to do it: it derives from the Package class that provides the implementation of the
18-
/// IVsPackage interface and uses the registration attributes defined in the framework to
19-
/// register itself and its components with the shell. These attributes tell the pkgdef creation
20-
/// utility what data to put into .pkgdef file.
21-
/// </para>
22-
/// <para>
23-
/// To get loaded into VS, the package must be referred by &lt;Asset Type="Microsoft.VisualStudio.VsPackage" ...&gt; in .vsixmanifest file.
24-
/// </para>
25-
/// </remarks>
2616
[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
2717
[Guid(BlazoryVSPackage.PackageGuidString)]
18+
[ProvideAutoLoad(UIContextGuids.NoSolution, PackageAutoLoadFlags.BackgroundLoad)]
19+
[ProvideAutoLoad(UIContextGuids.SolutionExists, PackageAutoLoadFlags.BackgroundLoad)]
2820
public sealed class BlazoryVSPackage : AsyncPackage
2921
{
3022
/// <summary>
31-
/// BlazoryVSPackage GUID string.
23+
/// Gets the BlazoryVSPackage GUID string.
3224
/// </summary>
3325
public const string PackageGuidString = "a0775924-0734-4872-b19d-3a1a56325a58";
3426

@@ -43,9 +35,44 @@ public sealed class BlazoryVSPackage : AsyncPackage
4335
/// <returns>A task representing the async work of package initialization, or an already completed task if there is none. Do not return null from this method.</returns>
4436
protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
4537
{
46-
// When initialized asynchronously, the current thread may be a background thread at this point.
47-
// Do any initialization that requires the UI thread after switching to the UI thread.
48-
await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
38+
try
39+
{
40+
// Gets the snippets from the specified JSON url.
41+
var csharpSnippets = await SnippetService.GetSnippetsAsync(BlazoryVSDefaults.CSharpSnippetsJsonUrl, BlazoryVSDefaults.CSharpSnippetLanguage, BlazoryVSDefaults.SnippetAuthor);
42+
var razorSnippets = await SnippetService.GetSnippetsAsync(BlazoryVSDefaults.RazorSnippetsJsonUrl, BlazoryVSDefaults.RazorSnippetLanguage, BlazoryVSDefaults.SnippetAuthor);
43+
44+
// Switch to main thread.
45+
await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
46+
47+
// Initialize settings manager.
48+
var settingsManager = new ShellSettingsManager(ServiceProvider.GlobalProvider);
49+
50+
// Removes the placeholder snippets.
51+
SnippetService.RemovePlaceholderSnippets(settingsManager);
52+
53+
// Gets the old snippets from the settings.
54+
var oldCsharpSnippets = SettingsService.GetLastSnippets(settingsManager, SnippetType.CSharp);
55+
var oldRazorSnippets = SettingsService.GetLastSnippets(settingsManager, SnippetType.Razor);
56+
57+
// Generates the snippet comparison reports.
58+
var csharpSnippetsComparison = SnippetService.GenerateSnippetComparisonReport(csharpSnippets, oldCsharpSnippets);
59+
var razorSnippetsComparison = SnippetService.GenerateSnippetComparisonReport(razorSnippets, oldRazorSnippets);
60+
61+
// Removes the snippets that are not in the JSON.
62+
SnippetService.RemoveSnippets(settingsManager, SnippetType.CSharp, csharpSnippetsComparison.SnippetsToBeDeleted);
63+
SnippetService.RemoveSnippets(settingsManager, SnippetType.Razor, razorSnippetsComparison.SnippetsToBeDeleted);
64+
65+
// Adds the snippets that are different than in the JSON.
66+
SnippetService.ApplySnippets(settingsManager, SnippetType.CSharp, csharpSnippetsComparison.SnippetsToBeEdited);
67+
SnippetService.ApplySnippets(settingsManager, SnippetType.Razor, razorSnippetsComparison.SnippetsToBeEdited);
68+
69+
// Update old snippets to the new ones.
70+
SettingsService.SaveToLastSnippets(settingsManager, SnippetType.CSharp, csharpSnippets);
71+
SettingsService.SaveToLastSnippets(settingsManager, SnippetType.Razor, razorSnippets);
72+
}
73+
catch
74+
{
75+
}
4976
}
5077

5178
#endregion

BlazoryVS/Enums/SnippetType.cs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace BlazoryVS.Enums
2+
{
3+
/// <summary>
4+
/// Represents the snippet type.
5+
/// </summary>
6+
internal enum SnippetType
7+
{
8+
CSharp,
9+
Razor
10+
}
11+
}

BlazoryVS/Helpers/CodeParserHelper.cs

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using BlazoryVS.Models;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Text.RegularExpressions;
6+
7+
namespace BlazoryVS.Helpers
8+
{
9+
/// <summary>
10+
/// Represents a helper class for parsing snippet code.
11+
/// </summary>
12+
internal static class CodeParserHelper
13+
{
14+
/// <summary>
15+
/// Parses the snippet code.
16+
/// </summary>
17+
/// <param name="code">Snippet code string.</param>
18+
/// <returns>A tuple containing parsed code string and an array of Literal objects.</returns>
19+
public static (string, Literal[]) ParseCode(string code)
20+
{
21+
try
22+
{
23+
var regex = new Regex(@"\$\{(\d+)(?:\:([^}|]+))?(\|([^}]+)\|)?\}|(\$0)");
24+
25+
var literalsDict = new Dictionary<string, Literal>();
26+
27+
var vsFormatted = regex.Replace(code, match =>
28+
{
29+
// Handle the special case of $0 (final cursor position in VS Code)
30+
if (match.Value == "$0")
31+
return "$end$";
32+
33+
var id = match.Groups[1].Value;
34+
var defaultValue = match.Groups[2].Value;
35+
36+
// Handle choices: We'll default to the first choice for VS
37+
// as VS doesn't support choices directly in the snippet
38+
if (string.IsNullOrWhiteSpace(defaultValue))
39+
{
40+
var choices = match.Groups[4].Value?.Split(',');
41+
if (choices != null && choices.Length > 0)
42+
{
43+
defaultValue = choices[0];
44+
}
45+
}
46+
47+
if (!literalsDict.ContainsKey(id))
48+
{
49+
literalsDict[id] = new Literal { Id = id, Default = defaultValue };
50+
}
51+
52+
return $"${id}$";
53+
});
54+
55+
return (vsFormatted, literalsDict.Values.ToArray());
56+
}
57+
catch
58+
{
59+
return (string.Empty, Array.Empty<Literal>());
60+
}
61+
}
62+
}
63+
}

BlazoryVS/Helpers/PathHelper.cs

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System.IO;
2+
using System.Linq;
3+
4+
namespace BlazoryVS.Helpers
5+
{
6+
/// <summary>
7+
/// Represents a helper class for path operations.
8+
/// </summary>
9+
internal class PathHelper
10+
{
11+
/// <summary>
12+
/// Sanitizes the file name.
13+
/// </summary>
14+
/// <param name="fileName">Proposed filename.</param>
15+
/// <returns>Sanitized file name string.</returns>
16+
public static string SanitizeFileName(string fileName)
17+
{
18+
var invalidChars = Path.GetInvalidFileNameChars();
19+
return new string(fileName.Where(ch => !invalidChars.Contains(ch)).ToArray());
20+
}
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using BlazoryVS.Models;
2+
using Newtonsoft.Json;
3+
using Newtonsoft.Json.Linq;
4+
using System;
5+
using System.Collections.Generic;
6+
7+
namespace BlazoryVS.Helpers
8+
{
9+
/// <summary>
10+
/// Represents the snippet deserialization helper class.
11+
/// </summary>
12+
internal static class SnippetDeserializationHelper
13+
{
14+
/// <summary>
15+
/// Deserializes the JSON string to a snippet array.
16+
/// </summary>
17+
/// <param name="json">JSON containing snippet objects.</param>
18+
/// <param name="language">Snippet language.</param>
19+
/// <param name="author">Snippet author.</param>
20+
/// <returns>Array of Snippet objects.</returns>
21+
public static Snippet[] DeserializeJsonToSnippets(string json, string language, string author)
22+
{
23+
try
24+
{
25+
if (string.IsNullOrWhiteSpace(json) || string.IsNullOrWhiteSpace(language) || string.IsNullOrWhiteSpace(author))
26+
return Array.Empty<Snippet>();
27+
28+
var jObjects = JsonConvert.DeserializeObject<Dictionary<string, JsonSnippet>>(json);
29+
var snippets = new List<Snippet>();
30+
31+
foreach (var kvp in jObjects)
32+
{
33+
var jsonSnippet = kvp.Value;
34+
35+
var code = string.Empty;
36+
37+
if (jsonSnippet.Body.Type == JTokenType.String)
38+
code = jsonSnippet.Body.ToString();
39+
else if (jsonSnippet.Body.Type == JTokenType.Array)
40+
code = string.Join("\n", jsonSnippet.Body.ToObject<string[]>());
41+
42+
var parsedCodeAndLiterals = CodeParserHelper.ParseCode(code);
43+
44+
snippets.Add(new Snippet
45+
{
46+
Name = kvp.Key,
47+
Author = author,
48+
Language = language,
49+
Prefix = jsonSnippet.Prefix,
50+
Description = jsonSnippet.Description,
51+
Code = parsedCodeAndLiterals.Item1,
52+
Literals = parsedCodeAndLiterals.Item2
53+
});
54+
}
55+
56+
return snippets.ToArray();
57+
}
58+
catch
59+
{
60+
return Array.Empty<Snippet>();
61+
}
62+
}
63+
}
64+
}

0 commit comments

Comments
 (0)