From e5e35899d0ce0db3d163873614ebc2f966ebd879 Mon Sep 17 00:00:00 2001 From: Emiliano Musso <58050537+EmilianoMusso@users.noreply.github.com> Date: Tue, 26 Nov 2024 20:21:03 +0100 Subject: [PATCH] General refactor and unit tests generation feature (#1) * chore: First tests generation implementation (wip) * chore: refactoring * chore: refactored tests prompt, increased version * chore: progress message revision * Added license file and set manifest variables * Added changelog; updated manifest --- CHANGELOG.md | 7 ++ EntwineLLM.sln | 2 + LICENSE.txt | 21 ++++ README.md | 4 +- src/EntwineLLM/Commands/BaseCommand.cs | 58 +++++++++++ .../Commands/GenerateTestsCommand.cs | 25 +++++ .../Commands/Interfaces/IBaseCommand.cs | 10 ++ .../Commands/RequestRefactorCommand.cs | 91 ++---------------- src/EntwineLLM/CommandsMenu.cs | 43 +++++++++ src/EntwineLLM/EntwineLLM.csproj | 27 +++++- .../{Windows => }/EntwineLLMOptions.cs | 0 src/EntwineLLM/EntwineLLMPackage.cs | 24 +++-- src/EntwineLLM/EntwineLLMPackage.vsct | 14 ++- src/EntwineLLM/Enums/RequestedCodeType.cs | 9 ++ src/EntwineLLM/Helpers/ProgressBarHelper.cs | 8 +- src/EntwineLLM/Helpers/PromptHelper.cs | 23 ++++- src/EntwineLLM/Helpers/RefactoringHelper.cs | 35 +++++-- src/EntwineLLM/Helpers/WindowHelper.cs | 24 +++++ src/EntwineLLM/LICENSE.txt | 21 ++++ src/EntwineLLM/Properties/AssemblyInfo.cs | 4 +- .../Properties/Resources.Designer.cs | 40 +++++--- src/EntwineLLM/Properties/Resources.resx | 62 ++++++++---- src/EntwineLLM/Resources/vs-entwine-menu.png | Bin 0 -> 3275 bytes src/EntwineLLM/source.extension.vsixmanifest | 9 +- 24 files changed, 410 insertions(+), 151 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 LICENSE.txt create mode 100644 src/EntwineLLM/Commands/BaseCommand.cs create mode 100644 src/EntwineLLM/Commands/GenerateTestsCommand.cs create mode 100644 src/EntwineLLM/Commands/Interfaces/IBaseCommand.cs create mode 100644 src/EntwineLLM/CommandsMenu.cs rename src/EntwineLLM/{Windows => }/EntwineLLMOptions.cs (100%) create mode 100644 src/EntwineLLM/Enums/RequestedCodeType.cs create mode 100644 src/EntwineLLM/Helpers/WindowHelper.cs create mode 100644 src/EntwineLLM/LICENSE.txt create mode 100644 src/EntwineLLM/Resources/vs-entwine-menu.png diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e944e4a --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,7 @@ +### v1.5.0 Unit tests generation +--- +- General refactor; implemented unit tests generation (fc8af9b) + +### v1.0.0 First release +--- +- First release; code writing and refactor functions enabled (02d319f) \ No newline at end of file diff --git a/EntwineLLM.sln b/EntwineLLM.sln index eaa0718..e913f1f 100644 --- a/EntwineLLM.sln +++ b/EntwineLLM.sln @@ -10,6 +10,8 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution items", "Solution items", "{CD17B50E-3006-4A0F-A203-FC6437B066AF}" ProjectSection(SolutionItems) = preProject .gitignore = .gitignore + CHANGELOG.md = CHANGELOG.md + LICENSE.txt = LICENSE.txt README.md = README.md EndProjectSection EndProject diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..b9d476f --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Emiliano Musso + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 2f2922d..8c4848a 100644 --- a/README.md +++ b/README.md @@ -18,12 +18,14 @@ After installing the EntwineLLM extension, its configuration options will be ava ![image](./src/EntwineLLM/Resources/vs-entwine-options.png) #### Using the extension -After installing the extension, the `Ask Entwine` command will be available in the Tools menu. By selecting a block of code or text related to the function you wish to create, the extension will call the LLM APIs available at the URL configured in the options. The extension will then present a window displaying the suggested code. If the user chooses to apply the suggestion, the new code will overwrite the originally selected text or code, seamlessly integrating the AI's assistance into the developer's workflow. +After installing the extension, the `Ask Entwine` command will be available in the Tools menu. By selecting a block of code or text related to the function you wish to create, the extension will call the LLM APIs available at the URL configured in the options. The extension will then present a window displaying the suggested code. If the user chooses to apply the suggestion, the new code will overwrite the originally selected text or code, seamlessly integrating the AI's assistance into the developer's workflow. Instead, the `Generate Unit Tests with EntwineLlm` command can be used to generate unit tests for every path on a selected block of code or function. The same flow applies: the extension will query the LLM, showing results on a separated window. ![image](./src/EntwineLLM/Resources/vs-entwine-suggestion.png) #### How it works The prompt for this extension is designed to focus specifically on C# development within the Microsoft .NET ecosystem, including the full framework, .NET Core, and related technologies like LINQ, Entity Framework, and ASP.NET Core. It has a strict set of rules: requests unrelated to C# coding are rejected with a raw `return null;` statement. If the request involves refactoring code, it follows Clean Code principles, ensuring readability, maintainability, and performance, with no extra explanations or comments provided. For new code requests, the same principles apply, with the emphasis on modularity, testability, and high performance. All code is provided in raw C# format, following strict style guidelines (Allman-style braces, vertical slicing) with no comments or additional context +![image](./src/EntwineLLM/Resources/vs-entwine-menu.png) + #### Why Entwine? The name Entwine and its logo, resembling the helix of DNA, symbolize the union of two forces: the natural intelligence and skills of the developer, and the artificial intelligence provided by the LLM. Just as DNA represents the intricate intertwining of biological elements that form life, Entwine reflects the harmonious connection between human creativity and AI-driven assistance. This synergy allows developers to harness the power of LLMs, enhancing their coding process while retaining their unique problem-solving abilities, creating a seamless collaboration between human and machine. \ No newline at end of file diff --git a/src/EntwineLLM/Commands/BaseCommand.cs b/src/EntwineLLM/Commands/BaseCommand.cs new file mode 100644 index 0000000..2297bd4 --- /dev/null +++ b/src/EntwineLLM/Commands/BaseCommand.cs @@ -0,0 +1,58 @@ +using EntwineLlm.Enums; +using EntwineLlm.Helpers; +using Microsoft.VisualStudio.Shell; +using System; +using System.Threading.Tasks; + +namespace EntwineLlm.Commands +{ + internal class BaseCommand + { + public AsyncPackage package; + + public string ActiveDocumentPath; + + public BaseCommand(AsyncPackage package) + { + this.package = package ?? throw new ArgumentNullException(nameof(package)); + } + + public string GetCurrentMethodCode() + { + ThreadHelper.ThrowIfNotOnUIThread(); + + if (!(Package.GetGlobalService(typeof(EnvDTE.DTE)) is EnvDTE.DTE dte)) + { + return string.Empty; + } + + var activeDocument = dte.ActiveDocument; + if (activeDocument == null) + { + return string.Empty; + } + + if (!(activeDocument.Selection is EnvDTE.TextSelection textSelection)) + { + return string.Empty; + } + + ActiveDocumentPath = dte.ActiveDocument.FullName; + return textSelection.Text; + } + + public async Task PerformRefactoringSuggestionAsync(RequestedCodeType codeType) + { + var message = "Waiting for LLM response (task requested: " + Enum.GetName(typeof(RequestedCodeType), codeType) + ") ..."; + + var progressBarHelper = new ProgressBarHelper(ServiceProvider.GlobalProvider); + progressBarHelper.StartIndeterminateDialog(message); + + var methodCode = GetCurrentMethodCode(); + var refactoringHelper = new RefactoringHelper(package); + await refactoringHelper.RequestCodeSuggestionsAsync(methodCode, ActiveDocumentPath, codeType); + + progressBarHelper.StopDialog(); + } + } +} diff --git a/src/EntwineLLM/Commands/GenerateTestsCommand.cs b/src/EntwineLLM/Commands/GenerateTestsCommand.cs new file mode 100644 index 0000000..f6547ff --- /dev/null +++ b/src/EntwineLLM/Commands/GenerateTestsCommand.cs @@ -0,0 +1,25 @@ +using EntwineLlm.Commands; +using EntwineLlm.Commands.Interfaces; +using Microsoft.VisualStudio.Shell; +using System; + +namespace EntwineLlm +{ + internal sealed class GenerateTestsCommand : BaseCommand, IBaseCommand + { + public int Id + { + get + { + return 251; + } + } + + public GenerateTestsCommand(AsyncPackage package) : base(package) { } + + public void Execute(object sender, EventArgs e) + { + _ = PerformRefactoringSuggestionAsync(Enums.RequestedCodeType.Test); + } + } +} diff --git a/src/EntwineLLM/Commands/Interfaces/IBaseCommand.cs b/src/EntwineLLM/Commands/Interfaces/IBaseCommand.cs new file mode 100644 index 0000000..6be67e9 --- /dev/null +++ b/src/EntwineLLM/Commands/Interfaces/IBaseCommand.cs @@ -0,0 +1,10 @@ +using System; + +namespace EntwineLlm.Commands.Interfaces +{ + internal interface IBaseCommand + { + int Id { get; } + void Execute(object sender, EventArgs e); + } +} diff --git a/src/EntwineLLM/Commands/RequestRefactorCommand.cs b/src/EntwineLLM/Commands/RequestRefactorCommand.cs index 6148691..92a5182 100644 --- a/src/EntwineLLM/Commands/RequestRefactorCommand.cs +++ b/src/EntwineLLM/Commands/RequestRefactorCommand.cs @@ -1,96 +1,25 @@ -using EntwineLlm.Helpers; +using EntwineLlm.Commands; +using EntwineLlm.Commands.Interfaces; using Microsoft.VisualStudio.Shell; -using Microsoft.VisualStudio.Shell.Interop; using System; -using System.ComponentModel.Design; -using System.Threading.Tasks; -using Task = System.Threading.Tasks.Task; namespace EntwineLlm { - internal sealed class RequestRefactorCommand + internal sealed class RequestRefactorCommand : BaseCommand, IBaseCommand { - public const int CommandId = 256; - - public static readonly Guid CommandSet = new Guid("714b6862-aad7-434e-8415-dd928555ba0e"); - - private readonly AsyncPackage package; - - private static string _activeDocumentPath; - - private RequestRefactorCommand(AsyncPackage package, OleMenuCommandService commandService) - { - this.package = package ?? throw new ArgumentNullException(nameof(package)); - commandService = commandService ?? throw new ArgumentNullException(nameof(commandService)); - - var menuCommandID = new CommandID(CommandSet, CommandId); - var menuItem = new MenuCommand(this.Execute, menuCommandID); - commandService.AddCommand(menuItem); - } - - public static RequestRefactorCommand Instance { get; private set; } - - public static async Task InitializeAsync(AsyncPackage package) + public int Id { - await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken); - - OleMenuCommandService commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService; - Instance = new RequestRefactorCommand(package, commandService); - } - - private void Execute(object sender, EventArgs e) - { - _ = PerformRefactoringSuggestionAsync(); - } - - private async Task PerformRefactoringSuggestionAsync() - { - var progressBarHelper = new ProgressBarHelper(ServiceProvider.GlobalProvider); - progressBarHelper.StartIndeterminateDialog(); - - var methodCode = GetCurrentMethodCode(); - var refactoringHelper = new RefactoringHelper(package); - await refactoringHelper.RequestAndShowRefactoringSuggestionAsync(methodCode, _activeDocumentPath); - - progressBarHelper.StopDialog(); - } - - private static string GetCurrentMethodCode() - { - ThreadHelper.ThrowIfNotOnUIThread(); - - if (!(Package.GetGlobalService(typeof(EnvDTE.DTE)) is EnvDTE.DTE dte)) - { - return string.Empty; - } - - var activeDocument = dte.ActiveDocument; - if (activeDocument == null) + get { - return string.Empty; + return 250; } - - if (!(activeDocument.Selection is EnvDTE.TextSelection textSelection)) - { - return string.Empty; - } - - _activeDocumentPath = dte.ActiveDocument.FullName; - return textSelection.Text; } - public async Task ShowToolWindowAsync() - { - await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - var window = await package.FindToolWindowAsync(typeof(RefactorSuggestionWindow), 0, true, package.DisposalToken); - if (window == null || window.Frame == null) - { - throw new NotSupportedException("Cannot create window"); - } + public RequestRefactorCommand(AsyncPackage package) : base(package) { } - var windowFrame = (IVsWindowFrame)window.Frame; - Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(windowFrame.Show()); - return window; + public void Execute(object sender, EventArgs e) + { + _ = PerformRefactoringSuggestionAsync(Enums.RequestedCodeType.Refactor); } } } diff --git a/src/EntwineLLM/CommandsMenu.cs b/src/EntwineLLM/CommandsMenu.cs new file mode 100644 index 0000000..a296116 --- /dev/null +++ b/src/EntwineLLM/CommandsMenu.cs @@ -0,0 +1,43 @@ +using EntwineLlm.Commands.Interfaces; +using Microsoft.VisualStudio.Shell; +using System; +using System.Collections.Generic; +using System.ComponentModel.Design; +using System.Threading.Tasks; + +namespace EntwineLlm +{ + internal class CommandsMenu + { + private static readonly Guid CommandSet = new Guid("714b6862-aad7-434e-8415-dd928555ba0e"); + private OleMenuCommandService CommandService; + + public OleMenuCommandService Service + { + get + { + return CommandService; + } + } + + public async Task InitializeAsync(AsyncPackage package) + { + CommandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService; + } + + public void AddCommand(IBaseCommand command) + { + var menuCommandID = new CommandID(CommandSet, command.Id); + var menuItem = new MenuCommand(command.Execute, menuCommandID); + CommandService.AddCommand(menuItem); + } + + public void AddCommands(IEnumerable commands) + { + foreach (var command in commands) + { + AddCommand(command); + } + } + } +} diff --git a/src/EntwineLLM/EntwineLLM.csproj b/src/EntwineLLM/EntwineLLM.csproj index f366eba..7f9a46d 100644 --- a/src/EntwineLLM/EntwineLLM.csproj +++ b/src/EntwineLLM/EntwineLLM.csproj @@ -49,11 +49,20 @@ 4 + + + + + + Component + + + True True @@ -62,9 +71,6 @@ AboutWindow.xaml - - Component - @@ -125,9 +131,20 @@ - + + Always + true + + + Always + true + + - + + Always + true + diff --git a/src/EntwineLLM/Windows/EntwineLLMOptions.cs b/src/EntwineLLM/EntwineLLMOptions.cs similarity index 100% rename from src/EntwineLLM/Windows/EntwineLLMOptions.cs rename to src/EntwineLLM/EntwineLLMOptions.cs diff --git a/src/EntwineLLM/EntwineLLMPackage.cs b/src/EntwineLLM/EntwineLLMPackage.cs index 55f8ea6..fe230b9 100644 --- a/src/EntwineLLM/EntwineLLMPackage.cs +++ b/src/EntwineLLM/EntwineLLMPackage.cs @@ -1,6 +1,8 @@ -using EntwineLlm.Models; +using EntwineLlm.Commands.Interfaces; +using EntwineLlm.Models; using Microsoft.VisualStudio.Shell; using System; +using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading; using Task = System.Threading.Tasks.Task; @@ -8,7 +10,7 @@ namespace EntwineLlm { [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] - [Guid(EntwineLlmPackage.PackageGuidString)] + [Guid(PackageGuidString)] [ProvideMenuResource("Menus.ctmenu", 1)] [ProvideToolWindow(typeof(RefactorSuggestionWindow))] [ProvideOptionPage(typeof(EntwineLlmOptions), "EntwineLlm", "Configuration", 0, 0, true)] @@ -16,14 +18,20 @@ public sealed class EntwineLlmPackage : AsyncPackage { public const string PackageGuidString = "3c995b0e-1f37-4cef-9ac7-9771b3fb6162"; - #region Package Members - protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) { - await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - await RequestRefactorCommand.InitializeAsync(this); - } + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var commandsMenu = new CommandsMenu(); + await commandsMenu.InitializeAsync(this); - #endregion + var commands = new List() + { + new RequestRefactorCommand(this), + new GenerateTestsCommand(this) + }; + + commandsMenu.AddCommands(commands); + } } } diff --git a/src/EntwineLLM/EntwineLLMPackage.vsct b/src/EntwineLLM/EntwineLLMPackage.vsct index ee5c7bb..f6a6960 100644 --- a/src/EntwineLLM/EntwineLLMPackage.vsct +++ b/src/EntwineLLM/EntwineLLMPackage.vsct @@ -5,12 +5,18 @@ - + @@ -21,7 +27,8 @@ - + + @@ -34,7 +41,8 @@ - + + diff --git a/src/EntwineLLM/Enums/RequestedCodeType.cs b/src/EntwineLLM/Enums/RequestedCodeType.cs new file mode 100644 index 0000000..096b0e9 --- /dev/null +++ b/src/EntwineLLM/Enums/RequestedCodeType.cs @@ -0,0 +1,9 @@ +namespace EntwineLlm.Enums +{ + internal enum RequestedCodeType : byte + { + Undefined = 0, + Refactor = 10, + Test = 20, + } +} diff --git a/src/EntwineLLM/Helpers/ProgressBarHelper.cs b/src/EntwineLLM/Helpers/ProgressBarHelper.cs index bea65aa..340b109 100644 --- a/src/EntwineLLM/Helpers/ProgressBarHelper.cs +++ b/src/EntwineLLM/Helpers/ProgressBarHelper.cs @@ -4,7 +4,7 @@ namespace EntwineLlm.Helpers { - public class ProgressBarHelper + internal class ProgressBarHelper { private readonly IVsThreadedWaitDialogFactory _dialogFactory; private IVsThreadedWaitDialog2 _dialog; @@ -18,19 +18,19 @@ public ProgressBarHelper(IServiceProvider serviceProvider) } } - public void StartIndeterminateDialog() + public void StartIndeterminateDialog(string message) { ThreadHelper.ThrowIfNotOnUIThread(); _dialogFactory.CreateInstance(out _dialog); _dialog?.StartWaitDialog( szWaitCaption: "EntwineLLM", - szWaitMessage: "Waiting for LLM response", + szWaitMessage: message, szProgressText: null, varStatusBmpAnim: null, szStatusBarText: null, - fIsCancelable: false, iDelayToShowDialog: 0, + fIsCancelable: false, fShowMarqueeProgress: true); } diff --git a/src/EntwineLLM/Helpers/PromptHelper.cs b/src/EntwineLLM/Helpers/PromptHelper.cs index ef919f5..801f980 100644 --- a/src/EntwineLLM/Helpers/PromptHelper.cs +++ b/src/EntwineLLM/Helpers/PromptHelper.cs @@ -4,16 +4,31 @@ namespace EntwineLlm.Helpers { internal static class PromptHelper { - public static string Generate(string model, string userCode) + public static string CreateForRefactor(string model, string userCode) { - var promptText = Properties.Resources.DefaultPrompt + var promptText = PreparePrompt(Properties.Resources.PromptForRefactor); + return ReplacePlaceholders(promptText, model, userCode); + } + + public static string CreateForTests(string model, string userCode) + { + var promptText = PreparePrompt(Properties.Resources.PromptForTests); + return ReplacePlaceholders(promptText, model, userCode); + } + + private static string PreparePrompt(string prompt) + { + return prompt .EscapeJsonString() .ReduceMultipleSpaces(); + } + private static string ReplacePlaceholders(string prompt, string model, string code) + { return Properties.Resources.LlmBaseRequest - .Replace("{PROMPT}", promptText) + .Replace("{PROMPT}", prompt) .Replace("{MODEL}", model) - .Replace("{CODE}", userCode.EscapeJsonString()); + .Replace("{CODE}", code.EscapeJsonString()); } } } diff --git a/src/EntwineLLM/Helpers/RefactoringHelper.cs b/src/EntwineLLM/Helpers/RefactoringHelper.cs index 05f288e..400165a 100644 --- a/src/EntwineLLM/Helpers/RefactoringHelper.cs +++ b/src/EntwineLLM/Helpers/RefactoringHelper.cs @@ -1,4 +1,5 @@ -using EntwineLlm.Helpers; +using EntwineLlm.Enums; +using EntwineLlm.Helpers; using EntwineLlm.Models; using Microsoft.VisualStudio.Shell; using Newtonsoft.Json.Linq; @@ -10,28 +11,46 @@ namespace EntwineLlm { - public class RefactoringHelper + internal class RefactoringHelper { private readonly EntwineLlmOptions _options; + private readonly AsyncPackage _package; public RefactoringHelper(AsyncPackage package) { + _package = package; _options = package.GetDialogPage(typeof(EntwineLlmOptions)) as EntwineLlmOptions; } - public async Task RequestAndShowRefactoringSuggestionAsync(string methodCode, string activeDocumentPath) + public async Task RequestCodeSuggestionsAsync( + string methodCode, + string activeDocumentPath, + RequestedCodeType codeType) { - var suggestion = await GetOllamaRefactoringSuggestionAsync(methodCode); + var suggestion = await GetCodeSuggestionsAsync(methodCode, codeType); await ShowRefactoringSuggestionAsync(suggestion, activeDocumentPath); } - private async Task GetOllamaRefactoringSuggestionAsync(string methodCode) + private async Task GetCodeSuggestionsAsync(string methodCode, RequestedCodeType codeType) { using (var client = new HttpClient()) { client.Timeout = _options.LlmRequestTimeOut; - var prompt = PromptHelper.Generate(_options.LlmModel, methodCode); + var prompt = string.Empty; + + switch (codeType) + { + case RequestedCodeType.Refactor: + prompt = PromptHelper.CreateForRefactor(_options.LlmModel, methodCode); + break; + case RequestedCodeType.Test: + prompt = PromptHelper.CreateForTests(_options.LlmModel, methodCode); + break; + default: + throw new ArgumentException("Invalid requested code type"); + } + var content = new StringContent(prompt, Encoding.UTF8, "application/json"); try @@ -53,9 +72,9 @@ private async Task GetOllamaRefactoringSuggestionAsync(string methodCode } } - private static async Task ShowRefactoringSuggestionAsync(string suggestion, string activeDocumentPath) + private async Task ShowRefactoringSuggestionAsync(string suggestion, string activeDocumentPath) { - ToolWindowPane window = await RequestRefactorCommand.Instance.ShowToolWindowAsync(); + ToolWindowPane window = await WindowHelper.ShowToolWindowAsync(_package); var control = (RefactorSuggestionWindowControl)window.Content; control.DisplaySuggestion(suggestion, activeDocumentPath); } diff --git a/src/EntwineLLM/Helpers/WindowHelper.cs b/src/EntwineLLM/Helpers/WindowHelper.cs new file mode 100644 index 0000000..7d73737 --- /dev/null +++ b/src/EntwineLLM/Helpers/WindowHelper.cs @@ -0,0 +1,24 @@ +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using System; +using System.Threading.Tasks; + +namespace EntwineLlm.Helpers +{ + internal static class WindowHelper + { + public static async Task ShowToolWindowAsync(AsyncPackage package) + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + var window = await package.FindToolWindowAsync(typeof(T), 0, true, package.DisposalToken); + if (window == null || window.Frame == null) + { + throw new NotSupportedException("Cannot create window"); + } + + var windowFrame = (IVsWindowFrame)window.Frame; + Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(windowFrame.Show()); + return window; + } + } +} diff --git a/src/EntwineLLM/LICENSE.txt b/src/EntwineLLM/LICENSE.txt new file mode 100644 index 0000000..b9d476f --- /dev/null +++ b/src/EntwineLLM/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Emiliano Musso + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/src/EntwineLLM/Properties/AssemblyInfo.cs b/src/EntwineLLM/Properties/AssemblyInfo.cs index e658102..309f35d 100644 --- a/src/EntwineLLM/Properties/AssemblyInfo.cs +++ b/src/EntwineLLM/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("1.5.0.0")] +[assembly: AssemblyFileVersion("1.5.0.0")] diff --git a/src/EntwineLLM/Properties/Resources.Designer.cs b/src/EntwineLLM/Properties/Resources.Designer.cs index 99ae712..2b39086 100644 --- a/src/EntwineLLM/Properties/Resources.Designer.cs +++ b/src/EntwineLLM/Properties/Resources.Designer.cs @@ -60,19 +60,6 @@ internal Resources() { } } - /// - /// Cerca una stringa localizzata simile a CONTEXT: You are a highly skilled and experienced C# developer with in-depth expertise in the Microsoft .NET ecosystem, including the full framework, .NET Core, and .NET. You are also proficient in related technologies, such as LINQ, Entity Framework, ASP.NET Core, and other commonly used tools and libraries in the C# development landscape. Your goal is to deliver optimal and professional-grade code solutions. - /// - ///RULES: - ///1. No explanation, comments, or additional text should be provided. - ///2. For requests co [stringa troncata]";. - /// - internal static string DefaultPrompt { - get { - return ResourceManager.GetString("DefaultPrompt", resourceCulture); - } - } - /// /// Cerca una risorsa localizzata di tipo System.Drawing.Bitmap. /// @@ -104,5 +91,32 @@ internal static string LlmBaseRequest { return ResourceManager.GetString("LlmBaseRequest", resourceCulture); } } + + /// + /// Cerca una stringa localizzata simile a CONTEXT: You are a highly skilled and experienced C# developer with in-depth expertise in the Microsoft .NET ecosystem, including the full framework, .NET Core, and .NET. You are also proficient in related technologies, such as LINQ, Entity Framework, ASP.NET Core, and other commonly used tools and libraries in the C# development landscape. Your goal is to deliver optimal and professional-grade code solutions. + /// + ///RULES: + ///1. No explanation, comments, or additional text should be provided. + ///2. For requests co [stringa troncata]";. + /// + internal static string PromptForRefactor { + get { + return ResourceManager.GetString("PromptForRefactor", resourceCulture); + } + } + + /// + /// Cerca una stringa localizzata simile a CONTEXT: You are a highly skilled and experienced C# developer specializing in crafting professional-grade automated tests. You have deep expertise in testing methodologies and frameworks, including MSTest, NUnit, and xUnit. Your goal is to produce high-quality, maintainable, and performant test suites that adhere to best practices for software quality assurance. + /// + ///RULES: + /// + ///- The tests must be clear, readable, and follow the Arrange-Act-Assert (AAA) pattern to ensure logical structure. + ///- Avoid comments i [stringa troncata]";. + /// + internal static string PromptForTests { + get { + return ResourceManager.GetString("PromptForTests", resourceCulture); + } + } } } diff --git a/src/EntwineLLM/Properties/Resources.resx b/src/EntwineLLM/Properties/Resources.resx index 55ee4a9..1a1b1e1 100644 --- a/src/EntwineLLM/Properties/Resources.resx +++ b/src/EntwineLLM/Properties/Resources.resx @@ -117,25 +117,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - CONTEXT: You are a highly skilled and experienced C# developer with in-depth expertise in the Microsoft .NET ecosystem, including the full framework, .NET Core, and .NET. You are also proficient in related technologies, such as LINQ, Entity Framework, ASP.NET Core, and other commonly used tools and libraries in the C# development landscape. Your goal is to deliver optimal and professional-grade code solutions. - -RULES: -1. No explanation, comments, or additional text should be provided. -2. For requests containing code [CODE] to refactor, you must: - - Refactor the code following Clean Code principles, emphasizing meaningful variable and method names, modularity (small functions), readability, maintainability, and high performance. - - Ensure the code is structured to be easily testable, avoiding tight coupling, using dependency injection where applicable, and separating concerns. - - Use Allman-style braces and prefer vertical slicing for method concatenation. - - Provide the refactored code as raw C# code without comments or any additional explanation. -3. For requests to create new code, you must: - - Generate the requested code adhering to Clean Code principles, ensuring readability, maintainability, and performance. - - Structure the code to be easily testable by employing best practices like dependency injection, abstraction, and clear separation of concerns. - - Use Allman-style braces and prefer vertical slicing for method concatenation. - - Provide the solution in raw C# code format without comments or explanations. - -OUTPUT FORMAT: -- Respond only with raw C# code. Do not include markdown, comments, or additional text. - ..\Resources\entwine-logo.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a @@ -156,4 +137,47 @@ OUTPUT FORMAT: ] } + + CONTEXT: You are a highly skilled and experienced C# developer with in-depth expertise in the Microsoft .NET ecosystem, including the full framework, .NET Core, and .NET. You are also proficient in related technologies, such as LINQ, Entity Framework, ASP.NET Core, and other commonly used tools and libraries in the C# development landscape. Your goal is to deliver optimal and professional-grade code solutions. + +RULES: +1. No explanation, comments, or additional text should be provided. +2. For requests containing code [CODE] to refactor, you must: + - Refactor the code following Clean Code principles, emphasizing meaningful variable and method names, modularity (small functions), readability, maintainability, and high performance. + - Ensure the code is structured to be easily testable, avoiding tight coupling, using dependency injection where applicable, and separating concerns. + - Use Allman-style braces and prefer vertical slicing for method concatenation. + - Provide the refactored code as raw C# code without comments or any additional explanation. +3. For requests to create new code, you must: + - Generate the requested code adhering to Clean Code principles, ensuring readability, maintainability, and performance. + - Structure the code to be easily testable by employing best practices like dependency injection, abstraction, and clear separation of concerns. + - Use Allman-style braces and prefer vertical slicing for method concatenation. + - Provide the solution in raw C# code format without comments or explanations. + +OUTPUT FORMAT: +- Respond only with raw C# code. Do not include markdown, comments, or additional text. + + + CONTEXT: You are a highly skilled and experienced C# developer specializing in crafting professional-grade automated tests. You have deep expertise in testing methodologies and frameworks, including MSTest, NUnit, and xUnit. Your goal is to produce high-quality, maintainable, and performant test suites that adhere to best practices for software quality assurance. + +RULES: + +- The tests must be clear, readable, and follow the Arrange-Act-Assert (AAA) pattern to ensure logical structure. +- Avoid comments in the test code. +- The code itself should be self-explanatory and expressive. +- Use meaningful and concise method names to clearly indicate the purpose of each test. +- The tests must cover both positive and negative scenarios, edge cases, and any specific behaviors related to the method or functionality being tested. +- Ensure the tests are isolated, avoiding dependencies on external systems unless explicitly required (e.g., mocking dependencies or using in-memory databases). +- Leverage parameterized tests or data-driven approaches where appropriate to avoid redundancy and improve maintainability. +- Use proper assertions to verify expected behavior, covering functional correctness, exceptions, and boundary conditions. +- If the provided code is incomplete or ambiguous, make reasonable assumptions to complete the tests but keep them realistic and within the described scope. +- Output only the raw C# test code, with no additional text, comments, or explanation. + +INPUT FORMAT: +- The method or functionality to be tested will be provided as C# code or a high-level description. + +OUTPUT FORMAT: +- Provide a test class containing professional-grade tests written in C#. Use MSTest, NUnit, or xUnit depending on the context. +- Use mock libraries such as Moq or FakeIt to mock services. +- The code must not include comments or additional text. + \ No newline at end of file diff --git a/src/EntwineLLM/Resources/vs-entwine-menu.png b/src/EntwineLLM/Resources/vs-entwine-menu.png new file mode 100644 index 0000000000000000000000000000000000000000..1559e79e896a3c40036d7423bbcdbda0dc27c8eb GIT binary patch literal 3275 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 literal 0 HcmV?d00001 diff --git a/src/EntwineLLM/source.extension.vsixmanifest b/src/EntwineLLM/source.extension.vsixmanifest index 7c5a24e..7d3ca22 100644 --- a/src/EntwineLLM/source.extension.vsixmanifest +++ b/src/EntwineLLM/source.extension.vsixmanifest @@ -1,10 +1,13 @@ - + EntwineLlm - Retrieval augmented generation (RAG) for C# coding support - true + Retrieval augmented generation (RAG) for C# coding support (code writing, refactor, unit tests generation) + LICENSE.txt + entwine-logo.ico + Resources\vs-entwine-suggestion.png + llm, rag, AI, llama, coding, assistant