Skip to content

Commit

Permalink
Merge pull request #2 from aglasencnik/dev
Browse files Browse the repository at this point in the history
Merge dev into main
  • Loading branch information
aglasencnik authored Nov 1, 2023
2 parents 0fc459b + b6d878b commit 7e801ad
Show file tree
Hide file tree
Showing 18 changed files with 842 additions and 23 deletions.
37 changes: 36 additions & 1 deletion BlazoryVS/BlazoryVS.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,49 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Compile Include="BlazoryVSDefaults.cs" />
<Compile Include="Enums\SnippetType.cs" />
<Compile Include="Helpers\CodeParserHelper.cs" />
<Compile Include="Helpers\PathHelper.cs" />
<Compile Include="Helpers\SnippetDeserializationHelper.cs" />
<Compile Include="Helpers\SnippetSerializationHelper.cs" />
<Compile Include="Models\JsonSnippet.cs" />
<Compile Include="Models\Literal.cs" />
<Compile Include="Models\Snippet.cs" />
<Compile Include="Models\SnippetComparisonReport.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="BlazoryVSPackage.cs" />
<Compile Include="Services\SettingsService.cs" />
<Compile Include="Services\SnippetService.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="Snippets\Blazory CSharp Snippets\placeholder.snippet">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<IncludeInVSIX>true</IncludeInVSIX>
</Content>
<Content Include="Snippets\Blazory Razor Snippets\placeholder.snippet">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<IncludeInVSIX>true</IncludeInVSIX>
</Content>
<None Include="source.extension.vsixmanifest">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.SDK" Version="17.0.32112.339" ExcludeAssets="runtime" />
<PackageReference Include="Community.VisualStudio.SourceGenerators">
<Version>1.0.3</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.SDK" Version="17.7.37357" ExcludeAssets="runtime">
<IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VSSDK.BuildTools" Version="17.7.2196" />
</ItemGroup>
<ItemGroup>
Expand All @@ -69,7 +99,12 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<IncludeInVSIX>true</IncludeInVSIX>
</Content>
<Content Include="Snippets\Snippets.pkgdef">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<IncludeInVSIX>true</IncludeInVSIX>
</Content>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Expand Down
79 changes: 79 additions & 0 deletions BlazoryVS/BlazoryVSDefaults.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
namespace BlazoryVS
{
/// <summary>
/// Represents the default constant values.
/// </summary>
internal class BlazoryVSDefaults
{
#region Path constants

/// <summary>
/// Gets the name of the folder that contains the snippets.
/// </summary>
public const string SnippetsFolderName = "Snippets";

/// <summary>
/// Gets the name of the folder that contains the C# snippets.
/// </summary>
public const string CSharpSnippetsFolderName = "Blazory CSharp Snippets";

/// <summary>
/// Gets the name of the folder that contains the Razor snippets.
/// </summary>
public const string RazorSnippetsFolderName = "Blazory Razor Snippets";

#endregion

#region Snippet constants

/// <summary>
/// Gets the name of the snippet author.
/// </summary>
public const string SnippetAuthor = "Blazory";

/// <summary>
/// Gets the name of the C# snippet language.
/// </summary>
public const string CSharpSnippetLanguage = "CSharp";

/// <summary>
/// Gets the name of the Razor snippet language.
/// </summary>
public const string RazorSnippetLanguage = "Razor";

/// <summary>
/// Gets the name of the placeholder snippet.
/// </summary>
public const string PlaceholderSnippetName = "placeholder.snippet";

#endregion

#region Blazory repository constants

/// <summary>
/// Gets the URL of the Blazory C# snippets JSON file.
/// </summary>
public const string CSharpSnippetsJsonUrl = "https://raw.githubusercontent.com/bartvanhoey/Blazory/master/snippets/csharp.json";

/// <summary>
/// Gets the URL of the Blazory Razor snippets JSON file.
/// </summary>
public const string RazorSnippetsJsonUrl = "https://raw.githubusercontent.com/bartvanhoey/Blazory/master/snippets/razor.json";

#endregion

#region Settings constants

/// <summary>
/// Gets the name of the C# snippets setting.
/// </summary>
public const string CSharpSnippetsSettingName = "CSharpSnippets";

/// <summary>
/// Gets the name of the Razor snippets setting.
/// </summary>
public const string RazorSnippetsSettingName = "RazorSnippets";

#endregion
}
}
67 changes: 47 additions & 20 deletions BlazoryVS/BlazoryVSPackage.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
using Microsoft.VisualStudio.Shell;
using BlazoryVS.Enums;
using BlazoryVS.Services;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell.Settings;
using System;
using System.Runtime.InteropServices;
using System.Threading;
Expand All @@ -7,28 +11,16 @@
namespace BlazoryVS
{
/// <summary>
/// This is the class that implements the package exposed by this assembly.
/// Represents the entry extension class.
/// </summary>
/// <remarks>
/// <para>
/// The minimum requirement for a class to be considered a valid package for Visual Studio
/// is to implement the IVsPackage interface and register itself with the shell.
/// This package uses the helper classes defined inside the Managed Package Framework (MPF)
/// to do it: it derives from the Package class that provides the implementation of the
/// IVsPackage interface and uses the registration attributes defined in the framework to
/// register itself and its components with the shell. These attributes tell the pkgdef creation
/// utility what data to put into .pkgdef file.
/// </para>
/// <para>
/// To get loaded into VS, the package must be referred by &lt;Asset Type="Microsoft.VisualStudio.VsPackage" ...&gt; in .vsixmanifest file.
/// </para>
/// </remarks>
[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
[Guid(BlazoryVSPackage.PackageGuidString)]
[ProvideAutoLoad(UIContextGuids.NoSolution, PackageAutoLoadFlags.BackgroundLoad)]
[ProvideAutoLoad(UIContextGuids.SolutionExists, PackageAutoLoadFlags.BackgroundLoad)]
public sealed class BlazoryVSPackage : AsyncPackage
{
/// <summary>
/// BlazoryVSPackage GUID string.
/// Gets the BlazoryVSPackage GUID string.
/// </summary>
public const string PackageGuidString = "a0775924-0734-4872-b19d-3a1a56325a58";

Expand All @@ -43,9 +35,44 @@ public sealed class BlazoryVSPackage : AsyncPackage
/// <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>
protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
{
// When initialized asynchronously, the current thread may be a background thread at this point.
// Do any initialization that requires the UI thread after switching to the UI thread.
await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
try
{
// Gets the snippets from the specified JSON url.
var csharpSnippets = await SnippetService.GetSnippetsAsync(BlazoryVSDefaults.CSharpSnippetsJsonUrl, BlazoryVSDefaults.CSharpSnippetLanguage, BlazoryVSDefaults.SnippetAuthor);
var razorSnippets = await SnippetService.GetSnippetsAsync(BlazoryVSDefaults.RazorSnippetsJsonUrl, BlazoryVSDefaults.RazorSnippetLanguage, BlazoryVSDefaults.SnippetAuthor);

// Switch to main thread.
await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);

// Initialize settings manager.
var settingsManager = new ShellSettingsManager(ServiceProvider.GlobalProvider);

// Removes the placeholder snippets.
SnippetService.RemovePlaceholderSnippets(settingsManager);

// Gets the old snippets from the settings.
var oldCsharpSnippets = SettingsService.GetLastSnippets(settingsManager, SnippetType.CSharp);
var oldRazorSnippets = SettingsService.GetLastSnippets(settingsManager, SnippetType.Razor);

// Generates the snippet comparison reports.
var csharpSnippetsComparison = SnippetService.GenerateSnippetComparisonReport(csharpSnippets, oldCsharpSnippets);
var razorSnippetsComparison = SnippetService.GenerateSnippetComparisonReport(razorSnippets, oldRazorSnippets);

// Removes the snippets that are not in the JSON.
SnippetService.RemoveSnippets(settingsManager, SnippetType.CSharp, csharpSnippetsComparison.SnippetsToBeDeleted);
SnippetService.RemoveSnippets(settingsManager, SnippetType.Razor, razorSnippetsComparison.SnippetsToBeDeleted);

// Adds the snippets that are different than in the JSON.
SnippetService.ApplySnippets(settingsManager, SnippetType.CSharp, csharpSnippetsComparison.SnippetsToBeEdited);
SnippetService.ApplySnippets(settingsManager, SnippetType.Razor, razorSnippetsComparison.SnippetsToBeEdited);

// Update old snippets to the new ones.
SettingsService.SaveToLastSnippets(settingsManager, SnippetType.CSharp, csharpSnippets);
SettingsService.SaveToLastSnippets(settingsManager, SnippetType.Razor, razorSnippets);
}
catch
{
}
}

#endregion
Expand Down
11 changes: 11 additions & 0 deletions BlazoryVS/Enums/SnippetType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace BlazoryVS.Enums
{
/// <summary>
/// Represents the snippet type.
/// </summary>
internal enum SnippetType
{
CSharp,
Razor
}
}
63 changes: 63 additions & 0 deletions BlazoryVS/Helpers/CodeParserHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using BlazoryVS.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace BlazoryVS.Helpers
{
/// <summary>
/// Represents a helper class for parsing snippet code.
/// </summary>
internal static class CodeParserHelper
{
/// <summary>
/// Parses the snippet code.
/// </summary>
/// <param name="code">Snippet code string.</param>
/// <returns>A tuple containing parsed code string and an array of Literal objects.</returns>
public static (string, Literal[]) ParseCode(string code)
{
try
{
var regex = new Regex(@"\$\{(\d+)(?:\:([^}|]+))?(\|([^}]+)\|)?\}|(\$0)");

var literalsDict = new Dictionary<string, Literal>();

var vsFormatted = regex.Replace(code, match =>
{
// Handle the special case of $0 (final cursor position in VS Code)
if (match.Value == "$0")
return "$end$";

var id = match.Groups[1].Value;
var defaultValue = match.Groups[2].Value;

// Handle choices: We'll default to the first choice for VS
// as VS doesn't support choices directly in the snippet
if (string.IsNullOrWhiteSpace(defaultValue))
{
var choices = match.Groups[4].Value?.Split(',');
if (choices != null && choices.Length > 0)
{
defaultValue = choices[0];
}
}

if (!literalsDict.ContainsKey(id))
{
literalsDict[id] = new Literal { Id = id, Default = defaultValue };
}

return $"${id}$";
});

return (vsFormatted, literalsDict.Values.ToArray());
}
catch
{
return (string.Empty, Array.Empty<Literal>());
}
}
}
}
22 changes: 22 additions & 0 deletions BlazoryVS/Helpers/PathHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.IO;
using System.Linq;

namespace BlazoryVS.Helpers
{
/// <summary>
/// Represents a helper class for path operations.
/// </summary>
internal class PathHelper
{
/// <summary>
/// Sanitizes the file name.
/// </summary>
/// <param name="fileName">Proposed filename.</param>
/// <returns>Sanitized file name string.</returns>
public static string SanitizeFileName(string fileName)
{
var invalidChars = Path.GetInvalidFileNameChars();
return new string(fileName.Where(ch => !invalidChars.Contains(ch)).ToArray());
}
}
}
64 changes: 64 additions & 0 deletions BlazoryVS/Helpers/SnippetDeserializationHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using BlazoryVS.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;

namespace BlazoryVS.Helpers
{
/// <summary>
/// Represents the snippet deserialization helper class.
/// </summary>
internal static class SnippetDeserializationHelper
{
/// <summary>
/// Deserializes the JSON string to a snippet array.
/// </summary>
/// <param name="json">JSON containing snippet objects.</param>
/// <param name="language">Snippet language.</param>
/// <param name="author">Snippet author.</param>
/// <returns>Array of Snippet objects.</returns>
public static Snippet[] DeserializeJsonToSnippets(string json, string language, string author)
{
try
{
if (string.IsNullOrWhiteSpace(json) || string.IsNullOrWhiteSpace(language) || string.IsNullOrWhiteSpace(author))
return Array.Empty<Snippet>();

var jObjects = JsonConvert.DeserializeObject<Dictionary<string, JsonSnippet>>(json);
var snippets = new List<Snippet>();

foreach (var kvp in jObjects)
{
var jsonSnippet = kvp.Value;

var code = string.Empty;

if (jsonSnippet.Body.Type == JTokenType.String)
code = jsonSnippet.Body.ToString();
else if (jsonSnippet.Body.Type == JTokenType.Array)
code = string.Join("\n", jsonSnippet.Body.ToObject<string[]>());

var parsedCodeAndLiterals = CodeParserHelper.ParseCode(code);

snippets.Add(new Snippet
{
Name = kvp.Key,
Author = author,
Language = language,
Prefix = jsonSnippet.Prefix,
Description = jsonSnippet.Description,
Code = parsedCodeAndLiterals.Item1,
Literals = parsedCodeAndLiterals.Item2
});
}

return snippets.ToArray();
}
catch
{
return Array.Empty<Snippet>();
}
}
}
}
Loading

0 comments on commit 7e801ad

Please sign in to comment.