Skip to content

Commit ed0147a

Browse files
tomaszgolebiowskiTomasz Gołębiowski
andauthored
Build-in prompts commands (#335)
This PR adds standard commands that automate and speed up workflow in Cody. You get the following core (built-in) prompts: - document-code - explain-code - find-code-smells - generate-unit-tests Commands are available when user is logged in. They are available in the editor's context menu. Commands can also be invoked using keyboard shortcuts, which can be configured in Visual Studio settings. ## Test plan **Scenario I** 1. Select a block of code in the editor (e.g. one function) 2. Click right mouse button and choose Cody > Generate unit tests 3. Make sure new chat with generated unit tests code appears **Scenario II** 1. Select a block of code in the editor (e.g. one function) 2. Click right mouse button and choose Cody > Explain code 3. Make sure new chat with generated unit tests code appears **Scenario III** 1. Select a block of code in the editor (e.g. one function) 2. Click right mouse button and choose Cody > Find Code Smells 3. Make sure new chat with generated unit tests code appears **Scenario IV** 1. Select a block of code in the editor (e.g. one function) 2. Click right mouse button and choose Cody > Document Code 3. Progress animation should appear on the status bar 4. Make sure that code documentation appears in code editor <!-- REQUIRED; info at https://docs-legacy.sourcegraph.com/dev/background-information/testing_principles --> --------- Co-authored-by: Tomasz Gołębiowski <[email protected]>
1 parent 6f76a04 commit ed0147a

File tree

11 files changed

+378
-152
lines changed

11 files changed

+378
-152
lines changed

src/Cody.Core/Agent/IAgentService.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ public interface IAgentService
8080
[AgentCall("testing/workspaceDocuments")]
8181
Task<GetDocumentsResult> GetWorkspaceDocuments(GetDocumentsParams documents);
8282

83+
[AgentCall("command/execute")]
84+
Task CommandExecute(ExecuteCommandParams command);
85+
8386
//---------------------------------------------------------
8487
// For notifications return type MUST be void!
8588
//---------------------------------------------------------
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace Cody.Core.Agent.Protocol
8+
{
9+
public class ExecuteCommandParams
10+
{
11+
public string Command { get; set; }
12+
public object[] Arguments { get; set; }
13+
}
14+
}

src/Cody.Core/Agent/TextDocumentNotificationHandlers.cs

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
using System;
21
using Cody.Core.Agent.Protocol;
32
using Cody.Core.Common;
43
using Cody.Core.Infrastructure;
54
using Cody.Core.Logging;
5+
using System;
66

77
namespace Cody.Core.Agent
88
{
@@ -11,12 +11,15 @@ public class TextDocumentNotificationHandlers
1111
private readonly IDocumentService documentService;
1212
private readonly IFileDialogService fileDialogService;
1313
private readonly ILog logger;
14+
private readonly IStatusbarService statusbarService;
1415

15-
public TextDocumentNotificationHandlers(IDocumentService documentService, IFileDialogService fileDialogService, ILog logger)
16+
public TextDocumentNotificationHandlers(IDocumentService documentService,
17+
IFileDialogService fileDialogService, IStatusbarService statusbarService, ILog logger)
1618
{
1719
this.documentService = documentService;
1820
this.fileDialogService = fileDialogService;
1921
this.logger = logger;
22+
this.statusbarService = statusbarService;
2023
}
2124

2225
[AgentCallback("window/showSaveDialog", deserializeToSingleObject: true)]
@@ -50,9 +53,13 @@ public bool Edit(TextDocumentEditParams textDocumentEdit)
5053
catch (Exception ex)
5154
{
5255
logger.Error($"Document edit failed for '{textDocumentEdit.Uri}'", ex);
56+
return false;
57+
}
58+
finally
59+
{
60+
statusbarService.StopProgressAnimation();
5361
}
5462

55-
return false;
5663
}
5764

5865
[AgentCallback("textDocument/show", deserializeToSingleObject: true)]
@@ -80,39 +87,44 @@ public bool WorkspaceEdit(WorkspaceEditParams workspaceEdit)
8087
return false;
8188
}
8289

83-
foreach (var operation in workspaceEdit.Operations)
90+
try
8491
{
85-
bool result;
86-
try
92+
foreach (var operation in workspaceEdit.Operations)
8793
{
88-
switch (operation)
94+
bool result;
95+
try
8996
{
90-
case CreateFileOperation createFile:
91-
result = documentService.CreateDocument(createFile.Uri.ToWindowsPath(), createFile.TextContents, createFile.Options?.Overwrite ?? false);
92-
break;
93-
case RenameFileOperation renameFile:
94-
result = documentService.RenameDocument(renameFile.OldUri.ToWindowsPath(), renameFile.NewUri.ToWindowsPath());
95-
break;
96-
case DeleteFileOperation deleteFile:
97-
result = documentService.DeleteDocument(deleteFile.Uri.ToWindowsPath());
98-
break;
99-
case EditFileOperation editFile:
100-
result = documentService.EditTextInDocument(editFile.Uri.ToWindowsPath(), editFile.Edits);
101-
break;
102-
default:
103-
throw new NotSupportedException($"Not supported operation: {operation}");
97+
switch (operation)
98+
{
99+
case CreateFileOperation createFile:
100+
result = documentService.CreateDocument(createFile.Uri.ToWindowsPath(), createFile.TextContents, createFile.Options?.Overwrite ?? false);
101+
break;
102+
case RenameFileOperation renameFile:
103+
result = documentService.RenameDocument(renameFile.OldUri.ToWindowsPath(), renameFile.NewUri.ToWindowsPath());
104+
break;
105+
case DeleteFileOperation deleteFile:
106+
result = documentService.DeleteDocument(deleteFile.Uri.ToWindowsPath());
107+
break;
108+
case EditFileOperation editFile:
109+
result = documentService.EditTextInDocument(editFile.Uri.ToWindowsPath(), editFile.Edits);
110+
break;
111+
default:
112+
throw new NotSupportedException($"Not supported operation: {operation}");
113+
}
114+
}
115+
catch (Exception ex)
116+
{
117+
logger.Error($"Workspace operation ('{operation.Type}') failed for '{GetOperationUri(operation)}'", ex);
118+
return false;
104119
}
105-
}
106-
catch (Exception ex)
107-
{
108-
logger.Error($"Workspace operation ('{operation.Type}') failed for '{GetOperationUri(operation)}'", ex);
109-
return false;
110120
}
111121

112-
if (!result) return false;
122+
return true;
123+
}
124+
finally
125+
{
126+
statusbarService.StopProgressAnimation();
113127
}
114-
115-
return true;
116128
}
117129

118130
private string GetOperationUri(WorkspaceEditOperation operation)

src/Cody.Core/Cody.Core.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
<Compile Include="Agent\Protocol\AutoeditChanges.cs" />
6262
<Compile Include="Agent\Protocol\AutoeditImageDiff.cs" />
6363
<Compile Include="Agent\Protocol\ChatPanelInfo.cs" />
64+
<Compile Include="Agent\Protocol\ExecuteCommandParams.cs" />
6465
<Compile Include="Agent\Protocol\ShowWindowMessageParams.cs" />
6566
<Compile Include="Agent\Protocol\CodyFileRename.cs" />
6667
<Compile Include="Agent\Protocol\AutoeditTextDiff.cs" />

src/Cody.Core/Infrastructure/IStatusbarService.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,9 @@ namespace Cody.Core.Infrastructure
99
public interface IStatusbarService
1010
{
1111
void SetText(string text);
12+
13+
void StartProgressAnimation();
14+
15+
void StopProgressAnimation();
1216
}
1317
}

src/Cody.VisualStudio/Cody.VisualStudio.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
<Compile Include="Client\NameTransformer.cs" />
5454
<Compile Include="Client\RemoteAgentConnector.cs" />
5555
<Compile Include="Client\TraceJsonRpc.cs" />
56+
<Compile Include="CodyPackage.Commands.cs" />
5657
<Compile Include="CodyPackage.ErrorHandling.cs" />
5758
<Compile Include="CodyToolWindow.cs" />
5859
<Compile Include="Infrastructure\LoggerFactory.cs" />
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
using Cody.Core.Agent.Protocol;
2+
using Microsoft.VisualStudio;
3+
using Microsoft.VisualStudio.Shell;
4+
using Microsoft.VisualStudio.Shell.Interop;
5+
using System;
6+
using System.Collections.Generic;
7+
using System.ComponentModel.Design;
8+
using System.Linq;
9+
using System.Text;
10+
using System.Threading.Tasks;
11+
12+
namespace Cody.VisualStudio
13+
{
14+
public partial class CodyPackage
15+
{
16+
private readonly Dictionary<int, CodyCommand> codyCommands = new Dictionary<int, CodyCommand>();
17+
18+
private void InitOleMenu()
19+
{
20+
AddCommand(CommandIds.CodyToolWindow, null, ShowToolWindow, true);
21+
AddCommand(CommandIds.DocumentCodeCommandId, "cody.command.document-code", InvokeCodyCommand, false);
22+
AddCommand(CommandIds.GenerateUnitTestsCommandId, "cody.command.unit-tests", InvokeCodyCommand, false);
23+
AddCommand(CommandIds.ExplainCodeCommandId, "cody.command.explain-code", InvokeCodyCommand, false);
24+
AddCommand(CommandIds.FindCodeSmellsCommandId, "cody.command.smell-code", InvokeCodyCommand, false);
25+
}
26+
27+
private void AddCommand(int commandId, string commandName, EventHandler handler, bool enabled)
28+
{
29+
try
30+
{
31+
var command = new CommandID(Guids.CodyPackageCommandSet, commandId);
32+
var menuCommand = new MenuCommand(handler, command);
33+
menuCommand.Enabled = enabled;
34+
35+
codyCommands[commandId] = new CodyCommand { MenuCommand = menuCommand, CommandName = commandName };
36+
37+
OleMenuService.AddCommand(menuCommand);
38+
}
39+
catch (Exception ex)
40+
{
41+
Logger.Error($"Failed to add command {commandName}", ex);
42+
}
43+
}
44+
45+
public void EnableContextMenu(bool enabled)
46+
{
47+
codyCommands
48+
.Where(x => x.Value.CommandName != null)
49+
.ToList()
50+
.ForEach(x => x.Value.MenuCommand.Enabled = enabled);
51+
}
52+
53+
public async void InvokeCodyCommand(object sender, EventArgs eventArgs)
54+
{
55+
try
56+
{
57+
var menuCommand = sender as MenuCommand;
58+
if (menuCommand == null)
59+
{
60+
Logger.Error("Cannot get MenuCommand instance");
61+
return;
62+
}
63+
64+
var commandId = menuCommand.CommandID.ID;
65+
66+
if (codyCommands.TryGetValue(commandId, out var command))
67+
{
68+
if (commandId == CommandIds.ExplainCodeCommandId ||
69+
commandId == CommandIds.FindCodeSmellsCommandId ||
70+
commandId == CommandIds.GenerateUnitTestsCommandId)
71+
{
72+
Logger.Debug($"Showing the chat window for the {command} command");
73+
await ShowToolWindowAsync();
74+
}
75+
else
76+
{
77+
StatusbarService?.StartProgressAnimation();
78+
}
79+
80+
if (AgentClient != null)
81+
{
82+
Logger.Info($"Invoking command: {command}");
83+
await AgentService.CommandExecute(new ExecuteCommandParams
84+
{
85+
Command = command.CommandName
86+
});
87+
}
88+
else Logger.Warn($"AgentClient not jet initialized. Can't invoke command: {command}");
89+
}
90+
else Logger.Error($"Cant find command for id: {commandId}");
91+
}
92+
catch (Exception ex)
93+
{
94+
Logger.Error($"Failed to invoke command", ex);
95+
}
96+
97+
}
98+
99+
public async void ShowToolWindow(object sender, EventArgs eventArgs) => await ShowToolWindowAsync();
100+
101+
public async Task ShowToolWindowAsync()
102+
{
103+
try
104+
{
105+
Logger.Debug("Showing Tool Window ...");
106+
var window = await ShowToolWindowAsync(typeof(CodyToolWindow), 0, true, DisposalToken);
107+
if (window?.Frame is IVsWindowFrame windowFrame)
108+
{
109+
bool isVisible = windowFrame.IsVisible() == 0;
110+
bool isOnScreen = windowFrame.IsOnScreen(out int screenTmp) == 0 && screenTmp == 1;
111+
112+
Logger.Debug($"IsVisible:{isVisible} IsOnScreen:{isOnScreen}");
113+
114+
if (!isVisible || !isOnScreen)
115+
{
116+
ErrorHandler.ThrowOnFailure(windowFrame.Show());
117+
Logger.Debug("Shown.");
118+
119+
}
120+
}
121+
}
122+
catch (Exception ex)
123+
{
124+
Logger.Error("Cannot toggle Tool Window.", ex);
125+
}
126+
}
127+
128+
public class CodyCommand
129+
{
130+
public MenuCommand MenuCommand { get; set; }
131+
public string CommandName { get; set; }
132+
}
133+
}
134+
}

0 commit comments

Comments
 (0)