Skip to content

Commit e749875

Browse files
committed
"feat: enhance code action registration and add new tab opening functionality (#XXXX)"
1 parent 505284f commit e749875

File tree

7 files changed

+175
-51
lines changed

7 files changed

+175
-51
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ out
33
out-*
44
node_modules
55
coverage/
6+
.idea
67

78
.DS_Store
89

src/activate/registerCodeActions.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ import { ACTION_NAMES, COMMAND_IDS } from "../core/CodeActionProvider"
44
import { EditorUtils } from "../core/EditorUtils"
55
import { ClineProvider } from "../core/webview/ClineProvider"
66

7+
/**
8+
* Registers code actions for the CodeActionProvider.
9+
*
10+
* @param context - The extension context.
11+
*/
712
export const registerCodeActions = (context: vscode.ExtensionContext) => {
813
registerCodeActionPair(
914
context,
@@ -32,6 +37,15 @@ export const registerCodeActions = (context: vscode.ExtensionContext) => {
3237
registerCodeAction(context, COMMAND_IDS.ADD_TO_CONTEXT, "ADD_TO_CONTEXT")
3338
}
3439

40+
/**
41+
* Registers a code action command.
42+
*
43+
* @param context - The extension context.
44+
* @param command - The command ID to register.
45+
* @param promptType - The type of prompt to show, if any.
46+
* @param inputPrompt - The prompt to show when asking for user input.
47+
* @param inputPlaceholder - The placeholder to show in the input box.
48+
*/
3549
const registerCodeAction = (
3650
context: vscode.ExtensionContext,
3751
command: string,
@@ -76,6 +90,20 @@ const registerCodeAction = (
7690
)
7791
}
7892

93+
/**
94+
* Registers two code action commands.
95+
*
96+
* The first command is registered with the given {@link baseCommand} and
97+
* {@link promptType}. The second command is registered with
98+
* `baseCommand + "InCurrentTask"` and the same {@link promptType}.
99+
*
100+
* @param context - The extension context.
101+
* @param baseCommand - The base command to register.
102+
* @param promptType - The type of prompt to show, if any.
103+
* @param inputPrompt - The prompt to show when asking for user input.
104+
* @param inputPlaceholder - The placeholder to show in the input box.
105+
*/
106+
79107
const registerCodeActionPair = (
80108
context: vscode.ExtensionContext,
81109
baseCommand: string,

src/activate/registerCommands.ts

Lines changed: 14 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import * as vscode from "vscode"
2-
import delay from "delay"
2+
import { openClineInNewTab } from "../integrations/tab/OpenTab"
33

44
import { ClineProvider } from "../core/webview/ClineProvider"
55

6-
import { registerHumanRelayCallback, unregisterHumanRelayCallback, handleHumanRelayResponse } from "./humanRelay"
6+
import { handleHumanRelayResponse, registerHumanRelayCallback, unregisterHumanRelayCallback } from "./humanRelay"
77

88
// Store panel references in both modes
99
let sidebarPanel: vscode.WebviewView | undefined = undefined
@@ -39,14 +39,25 @@ export type RegisterCommandOptions = {
3939
provider: ClineProvider
4040
}
4141

42-
export const registerCommands = (options: RegisterCommandOptions) => {
42+
/**
43+
* Registers all commands for the extension.
44+
* @param {RegisterCommandOptions} options The options to register commands with.
45+
* @returns {void}
46+
*/
47+
export const registerCommands = (options: RegisterCommandOptions): void => {
4348
const { context, outputChannel } = options
4449

4550
for (const [command, callback] of Object.entries(getCommandsMap(options))) {
4651
context.subscriptions.push(vscode.commands.registerCommand(command, callback))
4752
}
4853
}
4954

55+
/**
56+
* Generates a map of command names to functions that handle those commands.
57+
*
58+
* @param {RegisterCommandOptions} options The options to register commands with.
59+
* @returns {Record<string, (...args: any[]) => void>} A map of command names to functions.
60+
*/
5061
const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOptions) => {
5162
return {
5263
"roo-cline.plusButtonClicked": async () => {
@@ -87,49 +98,3 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt
8798
"roo-cline.handleHumanRelayResponse": handleHumanRelayResponse,
8899
}
89100
}
90-
91-
const openClineInNewTab = async ({ context, outputChannel }: Omit<RegisterCommandOptions, "provider">) => {
92-
// (This example uses webviewProvider activation event which is necessary to
93-
// deserialize cached webview, but since we use retainContextWhenHidden, we
94-
// don't need to use that event).
95-
// https://github.com/microsoft/vscode-extension-samples/blob/main/webview-sample/src/extension.ts
96-
const tabProvider = new ClineProvider(context, outputChannel, "editor")
97-
const lastCol = Math.max(...vscode.window.visibleTextEditors.map((editor) => editor.viewColumn || 0))
98-
99-
// Check if there are any visible text editors, otherwise open a new group
100-
// to the right.
101-
const hasVisibleEditors = vscode.window.visibleTextEditors.length > 0
102-
103-
if (!hasVisibleEditors) {
104-
await vscode.commands.executeCommand("workbench.action.newGroupRight")
105-
}
106-
107-
const targetCol = hasVisibleEditors ? Math.max(lastCol + 1, 1) : vscode.ViewColumn.Two
108-
109-
const newPanel = vscode.window.createWebviewPanel(ClineProvider.tabPanelId, "Roo Code", targetCol, {
110-
enableScripts: true,
111-
retainContextWhenHidden: true,
112-
localResourceRoots: [context.extensionUri],
113-
})
114-
115-
// Save as tab type panel.
116-
setPanel(newPanel, "tab")
117-
118-
// TODO: Use better svg icon with light and dark variants (see
119-
// https://stackoverflow.com/questions/58365687/vscode-extension-iconpath).
120-
newPanel.iconPath = {
121-
light: vscode.Uri.joinPath(context.extensionUri, "assets", "icons", "rocket.png"),
122-
dark: vscode.Uri.joinPath(context.extensionUri, "assets", "icons", "rocket.png"),
123-
}
124-
125-
await tabProvider.resolveWebviewView(newPanel)
126-
127-
// Handle panel closing events.
128-
newPanel.onDidDispose(() => {
129-
setPanel(undefined, "tab")
130-
})
131-
132-
// Lock the editor group so clicking on files doesn't open them over the panel.
133-
await delay(100)
134-
await vscode.commands.executeCommand("workbench.action.lockEditorGroup")
135-
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import * as vscode from "vscode"
2+
import { ContextProxy } from "../../contextProxy"
3+
import { SecretKey } from "../../../shared/globalState"
4+
5+
// Define our custom secret key type
6+
type CustomSecretKey = "customInstructionsApiKey"
7+
8+
export class CustomInstructionsSubscriber {
9+
private contextProxy: ContextProxy
10+
11+
constructor(contextProxy: ContextProxy) {
12+
this.contextProxy = contextProxy
13+
}
14+
15+
public async handleMessage(message: any): Promise<void> {
16+
if (message.type === "customInstructions") {
17+
await this.updateCustomInstructions(message.text)
18+
}
19+
}
20+
21+
private async updateCustomInstructions(text: string): Promise<void> {
22+
// Save instructions to global state
23+
await this.contextProxy.updateGlobalState("customInstructions", text)
24+
25+
// If in development mode, log the update
26+
if (this.contextProxy.extensionMode === vscode.ExtensionMode.Development) {
27+
console.log("Custom instructions updated:", text)
28+
}
29+
30+
// Example of using secrets
31+
const apiKey = await this.contextProxy.getSecret("customInstructionsApiKey" as SecretKey)
32+
if (apiKey) {
33+
// Process with API key
34+
}
35+
}
36+
}

src/exports/api.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as vscode from "vscode"
33

44
import { ClineProvider } from "../core/webview/ClineProvider"
55

6-
import { RooCodeAPI, RooCodeEvents, ConfigurationValues, TokenUsage } from "./roo-code"
6+
import { ConfigurationValues, RooCodeAPI, RooCodeEvents, TokenUsage } from "./roo-code"
77
import { MessageHistory } from "./message-history"
88

99
export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
@@ -12,6 +12,12 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
1212
private readonly history: MessageHistory
1313
private readonly tokenUsage: Record<string, TokenUsage>
1414

15+
/**
16+
* Construct a new API object.
17+
*
18+
* @param outputChannel The output channel to print any internal logs to.
19+
* @param provider The ClineProvider to listen to events from.
20+
*/
1521
constructor(outputChannel: vscode.OutputChannel, provider: ClineProvider) {
1622
super()
1723

@@ -55,6 +61,10 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
5561
return cline.taskId
5662
}
5763

64+
/**
65+
* Returns the current task stack.
66+
* @returns An array of task IDs.
67+
*/
5868
public getCurrentTaskStack() {
5969
return this.provider.getCurrentTaskStack()
6070
}

src/extension.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export async function activate(context: vscode.ExtensionContext) {
7474
webviewOptions: { retainContextWhenHidden: true },
7575
}),
7676
)
77-
77+
// register the commands for the extension
7878
registerCommands({ context, outputChannel, provider })
7979

8080
/**
@@ -94,6 +94,12 @@ export async function activate(context: vscode.ExtensionContext) {
9494
* https://code.visualstudio.com/api/extension-guides/virtual-documents
9595
*/
9696
const diffContentProvider = new (class implements vscode.TextDocumentContentProvider {
97+
/**
98+
* Given a virtual uri, returns the contents of the text document for that uri.
99+
* The uri query is expected to contain the text document contents encoded in base64.
100+
* @param uri The virtual uri for which to return the contents.
101+
* @returns The text document contents.
102+
*/
97103
provideTextDocumentContent(uri: vscode.Uri): string {
98104
return Buffer.from(uri.query, "base64").toString("utf-8")
99105
}

src/integrations/tab/OpenTab.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { ClineProvider } from "../../core/webview/ClineProvider"
2+
import vscode from "vscode"
3+
import delay from "delay"
4+
import { RegisterCommandOptions, setPanel } from "../../activate/registerCommands"
5+
6+
/**
7+
* Opens a new tab for the Roo Code webview panel.
8+
*
9+
* It does this by:
10+
*
11+
* 1. Creating a new `ClineProvider` for the "editor" type.
12+
* 2. Determining the column to open the new panel in by taking the max of the
13+
* columns of all visible text editors and adding 1. If there are no visible
14+
* text editors, it opens a new group to the right.
15+
* 3. Creating a new webview panel with the determined column and options.
16+
* 4. Setting the panel as a tab type panel using `setPanel`.
17+
* 5. Setting the icon for the panel using a hardcoded svg icon (TODO: use a better
18+
* icon).
19+
* 6. Resolving the webview view for the new panel using `resolveWebviewView`.
20+
* 7. Handling panel closing events by setting the panel to undefined in the
21+
* `setPanel` function.
22+
* 8. Locking the editor group so clicking on files doesn't open them over the
23+
* panel.
24+
*
25+
* @param options - The options to pass to the `ClineProvider` constructor.
26+
*/
27+
export const openClineInNewTab = async ({ context, outputChannel }: Omit<RegisterCommandOptions, "provider">) => {
28+
// (This example uses webviewProvider activation event which is necessary to
29+
// deserialize cached webview, but since we use retainContextWhenHidden, we
30+
// don't need to use that event).
31+
// https://github.com/microsoft/vscode-extension-samples/blob/main/webview-sample/src/extension.ts
32+
const tabProvider = new ClineProvider(context, outputChannel, "editor")
33+
const lastCol = Math.max(...vscode.window.visibleTextEditors.map((editor) => editor.viewColumn || 0))
34+
35+
// Check if there are any visible text editors, otherwise open a new group
36+
// to the right.
37+
const hasVisibleEditors = vscode.window.visibleTextEditors.length > 0
38+
39+
if (!hasVisibleEditors) {
40+
await vscode.commands.executeCommand("workbench.action.newGroupRight")
41+
}
42+
43+
const targetCol = hasVisibleEditors ? Math.max(lastCol + 1, 1) : vscode.ViewColumn.Two
44+
45+
const newPanel = vscode.window.createWebviewPanel(ClineProvider.tabPanelId, "Roo Code", targetCol, {
46+
enableScripts: true,
47+
retainContextWhenHidden: true,
48+
localResourceRoots: [context.extensionUri],
49+
})
50+
51+
// Save as tab type panel.
52+
setPanel(newPanel, "tab")
53+
54+
// TODO: Use better svg icon with light and dark variants (see
55+
// https://stackoverflow.com/questions/58365687/vscode-extension-iconpath).
56+
newPanel.iconPath = {
57+
light: vscode.Uri.joinPath(context.extensionUri, "assets", "icons", "rocket.png"),
58+
dark: vscode.Uri.joinPath(context.extensionUri, "assets", "icons", "rocket.png"),
59+
}
60+
61+
await tabProvider.resolveWebviewView(newPanel)
62+
// Create and register the subscriber
63+
// const customInstructionsSubscriber = new CustomInstructionsSubscriber(contextProxy);
64+
// newPanel.webview.onDidReceiveMessage(
65+
// async (message) => {
66+
// await customInstructionsSubscriber.handleMessage(message);
67+
// }
68+
// );
69+
70+
// Handle panel closing events.
71+
newPanel.onDidDispose(() => {
72+
setPanel(undefined, "tab")
73+
})
74+
75+
// Lock the editor group so clicking on files doesn't open them over the panel.
76+
await delay(100)
77+
await vscode.commands.executeCommand("workbench.action.lockEditorGroup")
78+
}

0 commit comments

Comments
 (0)