mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
Renames
This commit is contained in:
parent
e97b9f9772
commit
7747bf0c6d
5 changed files with 406 additions and 413 deletions
16
README.md
16
README.md
|
|
@ -40,7 +40,7 @@ Hot off the heels of **v3.0** introducing Code, Architect, and Ask chat modes, o
|
|||
|
||||
You can now tailor the **role definition** and **custom instructions** for every chat mode to perfectly fit your workflow. Want to adjust Architect mode to focus more on system scalability? Or tweak Ask mode for deeper research queries? Done. Plus, you can define these via **mode-specific `.clinerules-[mode]` files**. You’ll find all of this in the new **Prompts** tab in the top menu.
|
||||
|
||||
The second big feature in this release is a complete revamp of **prompt enhancements**. This feature helps you craft messages to get even better results from Cline. Here’s what’s new:
|
||||
The second big feature in this release is a complete revamp of **prompt enhancements**. This feature helps you craft messages to get even better results from Roo. Here’s what’s new:
|
||||
|
||||
- Works with **any provider** and API configuration, not just OpenRouter.
|
||||
- Fully customizable prompts to match your unique needs.
|
||||
|
|
@ -48,28 +48,26 @@ The second big feature in this release is a complete revamp of **prompt enhancem
|
|||
|
||||
Whether you’re using GPT-4, other APIs, or switching configurations, this gives you total control over how your prompts are optimized.
|
||||
|
||||
As always, we’d love to hear your thoughts and ideas! What features do you want to see in **v3.2**? Drop by https://www.reddit.com/r/roocline and join the discussion - we're building Roo Cline together. 🚀
|
||||
|
||||
## New in 3.0 - Chat Modes!
|
||||
|
||||
You can now choose between different prompts for Roo Cline to better suit your workflow. Here’s what’s available:
|
||||
You can now choose between different prompts for Roo to better suit your workflow. Here’s what’s available:
|
||||
|
||||
- **Code:** (existing behavior) The default mode where Cline helps you write code and execute tasks.
|
||||
- **Code:** (existing behavior) The default mode where Roo helps you write code and execute tasks.
|
||||
|
||||
- **Architect:** "You are Cline, a software architecture expert..." Ideal for thinking through high-level technical design and system architecture. Can’t write code or run commands.
|
||||
- **Architect:** "You are Roo, a software architecture expert..." Ideal for thinking through high-level technical design and system architecture. Can’t write code or run commands.
|
||||
|
||||
- **Ask:** "You are Cline, a knowledgeable technical assistant..." Perfect for asking questions about the codebase or digging into concepts. Also can’t write code or run commands.
|
||||
- **Ask:** "You are Roo, a knowledgeable technical assistant..." Perfect for asking questions about the codebase or digging into concepts. Also can’t write code or run commands.
|
||||
|
||||
**Switching Modes:**
|
||||
It’s super simple! There’s a dropdown in the bottom left of the chat input to switch modes. Right next to it, you’ll find a way to switch between the API configuration profiles associated with the current mode (configured on the settings screen).
|
||||
|
||||
**Why Add This?**
|
||||
|
||||
- It keeps Cline from being overly eager to jump into solving problems when you just want to think or ask questions.
|
||||
- It keeps Roo from being overly eager to jump into solving problems when you just want to think or ask questions.
|
||||
- Each mode remembers the API configuration you last used with it. For example, you can use more thoughtful models like OpenAI o1 for Architect and Ask, while sticking with Sonnet or DeepSeek for coding tasks.
|
||||
- It builds on research suggesting better results when separating "thinking" from "coding," explained well in this very thoughtful [article](https://aider.chat/2024/09/26/architect.html) from aider.
|
||||
|
||||
Right now, switching modes is a manual process. In the future, we’d love to give Cline the ability to suggest mode switches based on context. For now, we’d really appreciate your feedback on this feature.
|
||||
Right now, switching modes is a manual process. In the future, we’d love to give Roo the ability to suggest mode switches based on context. For now, we’d really appreciate your feedback on this feature.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
188
package.json
188
package.json
|
|
@ -72,102 +72,102 @@
|
|||
"title": "New Task",
|
||||
"icon": "$(add)"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.mcpButtonClicked",
|
||||
"title": "MCP Servers",
|
||||
"icon": "$(server)"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.promptsButtonClicked",
|
||||
"title": "Prompts",
|
||||
"icon": "$(notebook)"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.historyButtonClicked",
|
||||
"title": "History",
|
||||
"icon": "$(history)"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.popoutButtonClicked",
|
||||
"title": "Open in Editor",
|
||||
"icon": "$(link-external)"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.settingsButtonClicked",
|
||||
"title": "Settings",
|
||||
"icon": "$(settings-gear)"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.openInNewTab",
|
||||
"title": "Open In New Tab",
|
||||
"category": "Roo Code"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.explainCode",
|
||||
"title": "Explain Code",
|
||||
"category": "Roo Cline"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.fixCode",
|
||||
"title": "Fix Code",
|
||||
"category": "Roo Cline"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.improveCode",
|
||||
"title": "Improve Code",
|
||||
"category": "Roo Cline"
|
||||
}
|
||||
{
|
||||
"command": "roo-cline.mcpButtonClicked",
|
||||
"title": "MCP Servers",
|
||||
"icon": "$(server)"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.promptsButtonClicked",
|
||||
"title": "Prompts",
|
||||
"icon": "$(notebook)"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.historyButtonClicked",
|
||||
"title": "History",
|
||||
"icon": "$(history)"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.popoutButtonClicked",
|
||||
"title": "Open in Editor",
|
||||
"icon": "$(link-external)"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.settingsButtonClicked",
|
||||
"title": "Settings",
|
||||
"icon": "$(settings-gear)"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.openInNewTab",
|
||||
"title": "Open In New Tab",
|
||||
"category": "Roo Code"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.explainCode",
|
||||
"title": "Explain Code",
|
||||
"category": "Roo Code"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.fixCode",
|
||||
"title": "Fix Code",
|
||||
"category": "Roo Code"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.improveCode",
|
||||
"title": "Improve Code",
|
||||
"category": "Roo Code"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
"editor/context": [
|
||||
{
|
||||
"command": "roo-cline.explainCode",
|
||||
"when": "editorHasSelection",
|
||||
"group": "Roo Cline@1"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.fixCode",
|
||||
"when": "editorHasSelection",
|
||||
"group": "Roo Cline@2"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.improveCode",
|
||||
"when": "editorHasSelection",
|
||||
"group": "Roo Cline@3"
|
||||
}
|
||||
],
|
||||
"view/title": [
|
||||
{
|
||||
"command": "roo-cline.plusButtonClicked",
|
||||
"group": "navigation@1",
|
||||
"when": "view == roo-cline.SidebarProvider"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.promptsButtonClicked",
|
||||
"group": "navigation@2",
|
||||
"when": "view == roo-cline.SidebarProvider"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.mcpButtonClicked",
|
||||
"group": "navigation@3",
|
||||
"when": "view == roo-cline.SidebarProvider"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.historyButtonClicked",
|
||||
"group": "navigation@4",
|
||||
"when": "view == roo-cline.SidebarProvider"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.popoutButtonClicked",
|
||||
"group": "navigation@5",
|
||||
"when": "view == roo-cline.SidebarProvider"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.settingsButtonClicked",
|
||||
"group": "navigation@6",
|
||||
"when": "view == roo-cline.SidebarProvider"
|
||||
}
|
||||
]
|
||||
"editor/context": [
|
||||
{
|
||||
"command": "roo-cline.explainCode",
|
||||
"when": "editorHasSelection",
|
||||
"group": "Roo Code@1"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.fixCode",
|
||||
"when": "editorHasSelection",
|
||||
"group": "Roo Code@2"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.improveCode",
|
||||
"when": "editorHasSelection",
|
||||
"group": "Roo Code@3"
|
||||
}
|
||||
],
|
||||
"view/title": [
|
||||
{
|
||||
"command": "roo-cline.plusButtonClicked",
|
||||
"group": "navigation@1",
|
||||
"when": "view == roo-cline.SidebarProvider"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.promptsButtonClicked",
|
||||
"group": "navigation@2",
|
||||
"when": "view == roo-cline.SidebarProvider"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.mcpButtonClicked",
|
||||
"group": "navigation@3",
|
||||
"when": "view == roo-cline.SidebarProvider"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.historyButtonClicked",
|
||||
"group": "navigation@4",
|
||||
"when": "view == roo-cline.SidebarProvider"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.popoutButtonClicked",
|
||||
"group": "navigation@5",
|
||||
"when": "view == roo-cline.SidebarProvider"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.settingsButtonClicked",
|
||||
"group": "navigation@6",
|
||||
"when": "view == roo-cline.SidebarProvider"
|
||||
}
|
||||
]
|
||||
},
|
||||
"configuration": {
|
||||
"title": "Roo Code",
|
||||
|
|
|
|||
|
|
@ -1,181 +1,179 @@
|
|||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import * as vscode from "vscode"
|
||||
import * as path from "path"
|
||||
|
||||
export const ACTION_NAMES = {
|
||||
EXPLAIN: 'Roo Cline: Explain Code',
|
||||
FIX: 'Roo Cline: Fix Code',
|
||||
IMPROVE: 'Roo Cline: Improve Code'
|
||||
} as const;
|
||||
EXPLAIN: "Roo Code: Explain Code",
|
||||
FIX: "Roo Code: Fix Code",
|
||||
IMPROVE: "Roo Code: Improve Code",
|
||||
} as const
|
||||
|
||||
const COMMAND_IDS = {
|
||||
EXPLAIN: 'roo-cline.explainCode',
|
||||
FIX: 'roo-cline.fixCode',
|
||||
IMPROVE: 'roo-cline.improveCode'
|
||||
} as const;
|
||||
EXPLAIN: "roo-cline.explainCode",
|
||||
FIX: "roo-cline.fixCode",
|
||||
IMPROVE: "roo-cline.improveCode",
|
||||
} as const
|
||||
|
||||
interface DiagnosticData {
|
||||
message: string;
|
||||
severity: vscode.DiagnosticSeverity;
|
||||
code?: string | number | { value: string | number; target: vscode.Uri };
|
||||
source?: string;
|
||||
range: vscode.Range;
|
||||
message: string
|
||||
severity: vscode.DiagnosticSeverity
|
||||
code?: string | number | { value: string | number; target: vscode.Uri }
|
||||
source?: string
|
||||
range: vscode.Range
|
||||
}
|
||||
|
||||
interface EffectiveRange {
|
||||
range: vscode.Range;
|
||||
text: string;
|
||||
range: vscode.Range
|
||||
text: string
|
||||
}
|
||||
|
||||
export class CodeActionProvider implements vscode.CodeActionProvider {
|
||||
public static readonly providedCodeActionKinds = [
|
||||
vscode.CodeActionKind.QuickFix,
|
||||
vscode.CodeActionKind.RefactorRewrite,
|
||||
];
|
||||
public static readonly providedCodeActionKinds = [
|
||||
vscode.CodeActionKind.QuickFix,
|
||||
vscode.CodeActionKind.RefactorRewrite,
|
||||
]
|
||||
|
||||
// Cache file paths for performance
|
||||
private readonly filePathCache = new WeakMap<vscode.TextDocument, string>();
|
||||
// Cache file paths for performance
|
||||
private readonly filePathCache = new WeakMap<vscode.TextDocument, string>()
|
||||
|
||||
private getEffectiveRange(
|
||||
document: vscode.TextDocument,
|
||||
range: vscode.Range | vscode.Selection
|
||||
): EffectiveRange | null {
|
||||
try {
|
||||
const selectedText = document.getText(range);
|
||||
if (selectedText) {
|
||||
return { range, text: selectedText };
|
||||
}
|
||||
private getEffectiveRange(
|
||||
document: vscode.TextDocument,
|
||||
range: vscode.Range | vscode.Selection,
|
||||
): EffectiveRange | null {
|
||||
try {
|
||||
const selectedText = document.getText(range)
|
||||
if (selectedText) {
|
||||
return { range, text: selectedText }
|
||||
}
|
||||
|
||||
const currentLine = document.lineAt(range.start.line);
|
||||
if (!currentLine.text.trim()) {
|
||||
return null;
|
||||
}
|
||||
const currentLine = document.lineAt(range.start.line)
|
||||
if (!currentLine.text.trim()) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Optimize range creation by checking bounds first
|
||||
const startLine = Math.max(0, currentLine.lineNumber - 1);
|
||||
const endLine = Math.min(document.lineCount - 1, currentLine.lineNumber + 1);
|
||||
|
||||
// Only create new positions if needed
|
||||
const effectiveRange = new vscode.Range(
|
||||
startLine === currentLine.lineNumber ? range.start : new vscode.Position(startLine, 0),
|
||||
endLine === currentLine.lineNumber ? range.end : new vscode.Position(endLine, document.lineAt(endLine).text.length)
|
||||
);
|
||||
// Optimize range creation by checking bounds first
|
||||
const startLine = Math.max(0, currentLine.lineNumber - 1)
|
||||
const endLine = Math.min(document.lineCount - 1, currentLine.lineNumber + 1)
|
||||
|
||||
return {
|
||||
range: effectiveRange,
|
||||
text: document.getText(effectiveRange)
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error getting effective range:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// Only create new positions if needed
|
||||
const effectiveRange = new vscode.Range(
|
||||
startLine === currentLine.lineNumber ? range.start : new vscode.Position(startLine, 0),
|
||||
endLine === currentLine.lineNumber
|
||||
? range.end
|
||||
: new vscode.Position(endLine, document.lineAt(endLine).text.length),
|
||||
)
|
||||
|
||||
private getFilePath(document: vscode.TextDocument): string {
|
||||
// Check cache first
|
||||
let filePath = this.filePathCache.get(document);
|
||||
if (filePath) {
|
||||
return filePath;
|
||||
}
|
||||
return {
|
||||
range: effectiveRange,
|
||||
text: document.getText(effectiveRange),
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error getting effective range:", error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri);
|
||||
if (!workspaceFolder) {
|
||||
filePath = document.uri.fsPath;
|
||||
} else {
|
||||
const relativePath = path.relative(workspaceFolder.uri.fsPath, document.uri.fsPath);
|
||||
filePath = (!relativePath || relativePath.startsWith('..')) ? document.uri.fsPath : relativePath;
|
||||
}
|
||||
private getFilePath(document: vscode.TextDocument): string {
|
||||
// Check cache first
|
||||
let filePath = this.filePathCache.get(document)
|
||||
if (filePath) {
|
||||
return filePath
|
||||
}
|
||||
|
||||
// Cache the result
|
||||
this.filePathCache.set(document, filePath);
|
||||
return filePath;
|
||||
} catch (error) {
|
||||
console.error('Error getting file path:', error);
|
||||
return document.uri.fsPath;
|
||||
}
|
||||
}
|
||||
try {
|
||||
const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri)
|
||||
if (!workspaceFolder) {
|
||||
filePath = document.uri.fsPath
|
||||
} else {
|
||||
const relativePath = path.relative(workspaceFolder.uri.fsPath, document.uri.fsPath)
|
||||
filePath = !relativePath || relativePath.startsWith("..") ? document.uri.fsPath : relativePath
|
||||
}
|
||||
|
||||
private createDiagnosticData(diagnostic: vscode.Diagnostic): DiagnosticData {
|
||||
return {
|
||||
message: diagnostic.message,
|
||||
severity: diagnostic.severity,
|
||||
code: diagnostic.code,
|
||||
source: diagnostic.source,
|
||||
range: diagnostic.range // Reuse the range object
|
||||
};
|
||||
}
|
||||
// Cache the result
|
||||
this.filePathCache.set(document, filePath)
|
||||
return filePath
|
||||
} catch (error) {
|
||||
console.error("Error getting file path:", error)
|
||||
return document.uri.fsPath
|
||||
}
|
||||
}
|
||||
|
||||
private createAction(
|
||||
title: string,
|
||||
kind: vscode.CodeActionKind,
|
||||
command: string,
|
||||
args: any[]
|
||||
): vscode.CodeAction {
|
||||
const action = new vscode.CodeAction(title, kind);
|
||||
action.command = { command, title, arguments: args };
|
||||
return action;
|
||||
}
|
||||
private createDiagnosticData(diagnostic: vscode.Diagnostic): DiagnosticData {
|
||||
return {
|
||||
message: diagnostic.message,
|
||||
severity: diagnostic.severity,
|
||||
code: diagnostic.code,
|
||||
source: diagnostic.source,
|
||||
range: diagnostic.range, // Reuse the range object
|
||||
}
|
||||
}
|
||||
|
||||
private hasIntersectingRange(range1: vscode.Range, range2: vscode.Range): boolean {
|
||||
// Optimize range intersection check
|
||||
return !(
|
||||
range2.end.line < range1.start.line ||
|
||||
range2.start.line > range1.end.line ||
|
||||
(range2.end.line === range1.start.line && range2.end.character < range1.start.character) ||
|
||||
(range2.start.line === range1.end.line && range2.start.character > range1.end.character)
|
||||
);
|
||||
}
|
||||
private createAction(title: string, kind: vscode.CodeActionKind, command: string, args: any[]): vscode.CodeAction {
|
||||
const action = new vscode.CodeAction(title, kind)
|
||||
action.command = { command, title, arguments: args }
|
||||
return action
|
||||
}
|
||||
|
||||
public provideCodeActions(
|
||||
document: vscode.TextDocument,
|
||||
range: vscode.Range | vscode.Selection,
|
||||
context: vscode.CodeActionContext
|
||||
): vscode.ProviderResult<(vscode.CodeAction | vscode.Command)[]> {
|
||||
try {
|
||||
const effectiveRange = this.getEffectiveRange(document, range);
|
||||
if (!effectiveRange) {
|
||||
return [];
|
||||
}
|
||||
private hasIntersectingRange(range1: vscode.Range, range2: vscode.Range): boolean {
|
||||
// Optimize range intersection check
|
||||
return !(
|
||||
range2.end.line < range1.start.line ||
|
||||
range2.start.line > range1.end.line ||
|
||||
(range2.end.line === range1.start.line && range2.end.character < range1.start.character) ||
|
||||
(range2.start.line === range1.end.line && range2.start.character > range1.end.character)
|
||||
)
|
||||
}
|
||||
|
||||
const filePath = this.getFilePath(document);
|
||||
const actions: vscode.CodeAction[] = [];
|
||||
public provideCodeActions(
|
||||
document: vscode.TextDocument,
|
||||
range: vscode.Range | vscode.Selection,
|
||||
context: vscode.CodeActionContext,
|
||||
): vscode.ProviderResult<(vscode.CodeAction | vscode.Command)[]> {
|
||||
try {
|
||||
const effectiveRange = this.getEffectiveRange(document, range)
|
||||
if (!effectiveRange) {
|
||||
return []
|
||||
}
|
||||
|
||||
// Create actions using helper method
|
||||
actions.push(this.createAction(
|
||||
ACTION_NAMES.EXPLAIN,
|
||||
vscode.CodeActionKind.QuickFix,
|
||||
COMMAND_IDS.EXPLAIN,
|
||||
[filePath, effectiveRange.text]
|
||||
));
|
||||
const filePath = this.getFilePath(document)
|
||||
const actions: vscode.CodeAction[] = []
|
||||
|
||||
// Only process diagnostics if they exist
|
||||
if (context.diagnostics.length > 0) {
|
||||
const relevantDiagnostics = context.diagnostics.filter(d =>
|
||||
this.hasIntersectingRange(effectiveRange.range, d.range)
|
||||
);
|
||||
// Create actions using helper method
|
||||
actions.push(
|
||||
this.createAction(ACTION_NAMES.EXPLAIN, vscode.CodeActionKind.QuickFix, COMMAND_IDS.EXPLAIN, [
|
||||
filePath,
|
||||
effectiveRange.text,
|
||||
]),
|
||||
)
|
||||
|
||||
if (relevantDiagnostics.length > 0) {
|
||||
const diagnosticMessages = relevantDiagnostics.map(this.createDiagnosticData);
|
||||
actions.push(this.createAction(
|
||||
ACTION_NAMES.FIX,
|
||||
vscode.CodeActionKind.QuickFix,
|
||||
COMMAND_IDS.FIX,
|
||||
[filePath, effectiveRange.text, diagnosticMessages]
|
||||
));
|
||||
}
|
||||
}
|
||||
// Only process diagnostics if they exist
|
||||
if (context.diagnostics.length > 0) {
|
||||
const relevantDiagnostics = context.diagnostics.filter((d) =>
|
||||
this.hasIntersectingRange(effectiveRange.range, d.range),
|
||||
)
|
||||
|
||||
actions.push(this.createAction(
|
||||
ACTION_NAMES.IMPROVE,
|
||||
vscode.CodeActionKind.RefactorRewrite,
|
||||
COMMAND_IDS.IMPROVE,
|
||||
[filePath, effectiveRange.text]
|
||||
));
|
||||
if (relevantDiagnostics.length > 0) {
|
||||
const diagnosticMessages = relevantDiagnostics.map(this.createDiagnosticData)
|
||||
actions.push(
|
||||
this.createAction(ACTION_NAMES.FIX, vscode.CodeActionKind.QuickFix, COMMAND_IDS.FIX, [
|
||||
filePath,
|
||||
effectiveRange.text,
|
||||
diagnosticMessages,
|
||||
]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return actions;
|
||||
} catch (error) {
|
||||
console.error('Error providing code actions:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
actions.push(
|
||||
this.createAction(ACTION_NAMES.IMPROVE, vscode.CodeActionKind.RefactorRewrite, COMMAND_IDS.IMPROVE, [
|
||||
filePath,
|
||||
effectiveRange.text,
|
||||
]),
|
||||
)
|
||||
|
||||
return actions
|
||||
} catch (error) {
|
||||
console.error("Error providing code actions:", error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,145 +1,147 @@
|
|||
import * as vscode from 'vscode';
|
||||
import { CodeActionProvider } from '../CodeActionProvider';
|
||||
import * as vscode from "vscode"
|
||||
import { CodeActionProvider } from "../CodeActionProvider"
|
||||
|
||||
// Mock VSCode API
|
||||
jest.mock('vscode', () => ({
|
||||
CodeAction: jest.fn().mockImplementation((title, kind) => ({
|
||||
title,
|
||||
kind,
|
||||
command: undefined
|
||||
})),
|
||||
CodeActionKind: {
|
||||
QuickFix: { value: 'quickfix' },
|
||||
RefactorRewrite: { value: 'refactor.rewrite' }
|
||||
},
|
||||
Range: jest.fn().mockImplementation((startLine, startChar, endLine, endChar) => ({
|
||||
start: { line: startLine, character: startChar },
|
||||
end: { line: endLine, character: endChar }
|
||||
})),
|
||||
Position: jest.fn().mockImplementation((line, character) => ({
|
||||
line,
|
||||
character
|
||||
})),
|
||||
workspace: {
|
||||
getWorkspaceFolder: jest.fn()
|
||||
},
|
||||
DiagnosticSeverity: {
|
||||
Error: 0,
|
||||
Warning: 1,
|
||||
Information: 2,
|
||||
Hint: 3
|
||||
}
|
||||
}));
|
||||
jest.mock("vscode", () => ({
|
||||
CodeAction: jest.fn().mockImplementation((title, kind) => ({
|
||||
title,
|
||||
kind,
|
||||
command: undefined,
|
||||
})),
|
||||
CodeActionKind: {
|
||||
QuickFix: { value: "quickfix" },
|
||||
RefactorRewrite: { value: "refactor.rewrite" },
|
||||
},
|
||||
Range: jest.fn().mockImplementation((startLine, startChar, endLine, endChar) => ({
|
||||
start: { line: startLine, character: startChar },
|
||||
end: { line: endLine, character: endChar },
|
||||
})),
|
||||
Position: jest.fn().mockImplementation((line, character) => ({
|
||||
line,
|
||||
character,
|
||||
})),
|
||||
workspace: {
|
||||
getWorkspaceFolder: jest.fn(),
|
||||
},
|
||||
DiagnosticSeverity: {
|
||||
Error: 0,
|
||||
Warning: 1,
|
||||
Information: 2,
|
||||
Hint: 3,
|
||||
},
|
||||
}))
|
||||
|
||||
describe('CodeActionProvider', () => {
|
||||
let provider: CodeActionProvider;
|
||||
let mockDocument: any;
|
||||
let mockRange: any;
|
||||
let mockContext: any;
|
||||
describe("CodeActionProvider", () => {
|
||||
let provider: CodeActionProvider
|
||||
let mockDocument: any
|
||||
let mockRange: any
|
||||
let mockContext: any
|
||||
|
||||
beforeEach(() => {
|
||||
provider = new CodeActionProvider();
|
||||
|
||||
// Mock document
|
||||
mockDocument = {
|
||||
getText: jest.fn(),
|
||||
lineAt: jest.fn(),
|
||||
lineCount: 10,
|
||||
uri: { fsPath: '/test/file.ts' }
|
||||
};
|
||||
beforeEach(() => {
|
||||
provider = new CodeActionProvider()
|
||||
|
||||
// Mock range
|
||||
mockRange = new vscode.Range(0, 0, 0, 10);
|
||||
// Mock document
|
||||
mockDocument = {
|
||||
getText: jest.fn(),
|
||||
lineAt: jest.fn(),
|
||||
lineCount: 10,
|
||||
uri: { fsPath: "/test/file.ts" },
|
||||
}
|
||||
|
||||
// Mock context
|
||||
mockContext = {
|
||||
diagnostics: []
|
||||
};
|
||||
});
|
||||
// Mock range
|
||||
mockRange = new vscode.Range(0, 0, 0, 10)
|
||||
|
||||
describe('getEffectiveRange', () => {
|
||||
it('should return selected text when available', () => {
|
||||
mockDocument.getText.mockReturnValue('selected text');
|
||||
|
||||
const result = (provider as any).getEffectiveRange(mockDocument, mockRange);
|
||||
|
||||
expect(result).toEqual({
|
||||
range: mockRange,
|
||||
text: 'selected text'
|
||||
});
|
||||
});
|
||||
// Mock context
|
||||
mockContext = {
|
||||
diagnostics: [],
|
||||
}
|
||||
})
|
||||
|
||||
it('should return null for empty line', () => {
|
||||
mockDocument.getText.mockReturnValue('');
|
||||
mockDocument.lineAt.mockReturnValue({ text: '', lineNumber: 0 });
|
||||
|
||||
const result = (provider as any).getEffectiveRange(mockDocument, mockRange);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
describe("getEffectiveRange", () => {
|
||||
it("should return selected text when available", () => {
|
||||
mockDocument.getText.mockReturnValue("selected text")
|
||||
|
||||
describe('getFilePath', () => {
|
||||
it('should return relative path when in workspace', () => {
|
||||
const mockWorkspaceFolder = {
|
||||
uri: { fsPath: '/test' }
|
||||
};
|
||||
(vscode.workspace.getWorkspaceFolder as jest.Mock).mockReturnValue(mockWorkspaceFolder);
|
||||
|
||||
const result = (provider as any).getFilePath(mockDocument);
|
||||
|
||||
expect(result).toBe('file.ts');
|
||||
});
|
||||
const result = (provider as any).getEffectiveRange(mockDocument, mockRange)
|
||||
|
||||
it('should return absolute path when not in workspace', () => {
|
||||
(vscode.workspace.getWorkspaceFolder as jest.Mock).mockReturnValue(null);
|
||||
|
||||
const result = (provider as any).getFilePath(mockDocument);
|
||||
|
||||
expect(result).toBe('/test/file.ts');
|
||||
});
|
||||
});
|
||||
expect(result).toEqual({
|
||||
range: mockRange,
|
||||
text: "selected text",
|
||||
})
|
||||
})
|
||||
|
||||
describe('provideCodeActions', () => {
|
||||
beforeEach(() => {
|
||||
mockDocument.getText.mockReturnValue('test code');
|
||||
mockDocument.lineAt.mockReturnValue({ text: 'test code', lineNumber: 0 });
|
||||
});
|
||||
it("should return null for empty line", () => {
|
||||
mockDocument.getText.mockReturnValue("")
|
||||
mockDocument.lineAt.mockReturnValue({ text: "", lineNumber: 0 })
|
||||
|
||||
it('should provide explain and improve actions by default', () => {
|
||||
const actions = provider.provideCodeActions(mockDocument, mockRange, mockContext);
|
||||
|
||||
expect(actions).toHaveLength(2);
|
||||
expect((actions as any)[0].title).toBe('Roo Cline: Explain Code');
|
||||
expect((actions as any)[1].title).toBe('Roo Cline: Improve Code');
|
||||
});
|
||||
const result = (provider as any).getEffectiveRange(mockDocument, mockRange)
|
||||
|
||||
it('should provide fix action when diagnostics exist', () => {
|
||||
mockContext.diagnostics = [{
|
||||
message: 'test error',
|
||||
severity: vscode.DiagnosticSeverity.Error,
|
||||
range: mockRange
|
||||
}];
|
||||
|
||||
const actions = provider.provideCodeActions(mockDocument, mockRange, mockContext);
|
||||
|
||||
expect(actions).toHaveLength(3);
|
||||
expect((actions as any).some((a: any) => a.title === 'Roo Cline: Fix Code')).toBe(true);
|
||||
});
|
||||
expect(result).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
it('should handle errors gracefully', () => {
|
||||
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
mockDocument.getText.mockImplementation(() => {
|
||||
throw new Error('Test error');
|
||||
});
|
||||
mockDocument.lineAt.mockReturnValue({ text: 'test', lineNumber: 0 });
|
||||
|
||||
const actions = provider.provideCodeActions(mockDocument, mockRange, mockContext);
|
||||
|
||||
expect(actions).toEqual([]);
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith('Error getting effective range:', expect.any(Error));
|
||||
|
||||
consoleErrorSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe("getFilePath", () => {
|
||||
it("should return relative path when in workspace", () => {
|
||||
const mockWorkspaceFolder = {
|
||||
uri: { fsPath: "/test" },
|
||||
}
|
||||
;(vscode.workspace.getWorkspaceFolder as jest.Mock).mockReturnValue(mockWorkspaceFolder)
|
||||
|
||||
const result = (provider as any).getFilePath(mockDocument)
|
||||
|
||||
expect(result).toBe("file.ts")
|
||||
})
|
||||
|
||||
it("should return absolute path when not in workspace", () => {
|
||||
;(vscode.workspace.getWorkspaceFolder as jest.Mock).mockReturnValue(null)
|
||||
|
||||
const result = (provider as any).getFilePath(mockDocument)
|
||||
|
||||
expect(result).toBe("/test/file.ts")
|
||||
})
|
||||
})
|
||||
|
||||
describe("provideCodeActions", () => {
|
||||
beforeEach(() => {
|
||||
mockDocument.getText.mockReturnValue("test code")
|
||||
mockDocument.lineAt.mockReturnValue({ text: "test code", lineNumber: 0 })
|
||||
})
|
||||
|
||||
it("should provide explain and improve actions by default", () => {
|
||||
const actions = provider.provideCodeActions(mockDocument, mockRange, mockContext)
|
||||
|
||||
expect(actions).toHaveLength(2)
|
||||
expect((actions as any)[0].title).toBe("Roo Code: Explain Code")
|
||||
expect((actions as any)[1].title).toBe("Roo Code: Improve Code")
|
||||
})
|
||||
|
||||
it("should provide fix action when diagnostics exist", () => {
|
||||
mockContext.diagnostics = [
|
||||
{
|
||||
message: "test error",
|
||||
severity: vscode.DiagnosticSeverity.Error,
|
||||
range: mockRange,
|
||||
},
|
||||
]
|
||||
|
||||
const actions = provider.provideCodeActions(mockDocument, mockRange, mockContext)
|
||||
|
||||
expect(actions).toHaveLength(3)
|
||||
expect((actions as any).some((a: any) => a.title === "Roo Code: Fix Code")).toBe(true)
|
||||
})
|
||||
|
||||
it("should handle errors gracefully", () => {
|
||||
const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => {})
|
||||
mockDocument.getText.mockImplementation(() => {
|
||||
throw new Error("Test error")
|
||||
})
|
||||
mockDocument.lineAt.mockReturnValue({ text: "test", lineNumber: 0 })
|
||||
|
||||
const actions = provider.provideCodeActions(mockDocument, mockRange, mockContext)
|
||||
|
||||
expect(actions).toEqual([])
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith("Error getting effective range:", expect.any(Error))
|
||||
|
||||
consoleErrorSpy.mockRestore()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ const dotenv = require("dotenv")
|
|||
const testEnvPath = path.join(__dirname, ".test_env")
|
||||
dotenv.config({ path: testEnvPath })
|
||||
|
||||
suite("Roo Cline Extension Test Suite", () => {
|
||||
vscode.window.showInformationMessage("Starting Roo Cline extension tests.")
|
||||
suite("Roo Code Extension Test Suite", () => {
|
||||
vscode.window.showInformationMessage("Starting Roo Code extension tests.")
|
||||
|
||||
test("Extension should be present", () => {
|
||||
const extension = vscode.extensions.getExtension("RooVeterinaryInc.roo-cline")
|
||||
|
|
@ -117,16 +117,16 @@ suite("Roo Cline Extension Test Suite", () => {
|
|||
|
||||
// Test core commands are registered
|
||||
const expectedCommands = [
|
||||
'roo-cline.plusButtonClicked',
|
||||
'roo-cline.mcpButtonClicked',
|
||||
'roo-cline.historyButtonClicked',
|
||||
'roo-cline.popoutButtonClicked',
|
||||
'roo-cline.settingsButtonClicked',
|
||||
'roo-cline.openInNewTab',
|
||||
'roo-cline.explainCode',
|
||||
'roo-cline.fixCode',
|
||||
'roo-cline.improveCode'
|
||||
];
|
||||
"roo-cline.plusButtonClicked",
|
||||
"roo-cline.mcpButtonClicked",
|
||||
"roo-cline.historyButtonClicked",
|
||||
"roo-cline.popoutButtonClicked",
|
||||
"roo-cline.settingsButtonClicked",
|
||||
"roo-cline.openInNewTab",
|
||||
"roo-cline.explainCode",
|
||||
"roo-cline.fixCode",
|
||||
"roo-cline.improveCode",
|
||||
]
|
||||
|
||||
for (const cmd of expectedCommands) {
|
||||
assert.strictEqual(commands.includes(cmd), true, `Command ${cmd} should be registered`)
|
||||
|
|
@ -136,7 +136,7 @@ suite("Roo Cline Extension Test Suite", () => {
|
|||
test("Views should be registered", () => {
|
||||
const view = vscode.window.createWebviewPanel(
|
||||
"roo-cline.SidebarProvider",
|
||||
"Roo Cline",
|
||||
"Roo Code",
|
||||
vscode.ViewColumn.One,
|
||||
{},
|
||||
)
|
||||
|
|
@ -184,17 +184,12 @@ suite("Roo Cline Extension Test Suite", () => {
|
|||
|
||||
// Create webview panel with development options
|
||||
const extensionUri = extension.extensionUri
|
||||
const panel = vscode.window.createWebviewPanel(
|
||||
"roo-cline.SidebarProvider",
|
||||
"Roo Cline",
|
||||
vscode.ViewColumn.One,
|
||||
{
|
||||
enableScripts: true,
|
||||
enableCommandUris: true,
|
||||
retainContextWhenHidden: true,
|
||||
localResourceRoots: [extensionUri],
|
||||
},
|
||||
)
|
||||
const panel = vscode.window.createWebviewPanel("roo-cline.SidebarProvider", "Roo Code", vscode.ViewColumn.One, {
|
||||
enableScripts: true,
|
||||
enableCommandUris: true,
|
||||
retainContextWhenHidden: true,
|
||||
localResourceRoots: [extensionUri],
|
||||
})
|
||||
|
||||
try {
|
||||
// Initialize webview with development context
|
||||
|
|
@ -299,7 +294,7 @@ suite("Roo Cline Extension Test Suite", () => {
|
|||
m.type === "say" && m.text && m.text.toLowerCase().includes("cline"),
|
||||
)
|
||||
if (hasResponse) {
|
||||
console.log('Found response containing "Cline" in provider messages')
|
||||
console.log('Found response containing "Roo Code" in provider messages')
|
||||
responseReceived = true
|
||||
break
|
||||
}
|
||||
|
|
@ -308,14 +303,14 @@ suite("Roo Cline Extension Test Suite", () => {
|
|||
// Check provider.cline.clineMessages
|
||||
const clineMessages = provider.cline?.clineMessages
|
||||
if (clineMessages && clineMessages.length > 0) {
|
||||
console.log("Cline messages:", JSON.stringify(clineMessages, null, 2))
|
||||
console.log("Roo Code messages:", JSON.stringify(clineMessages, null, 2))
|
||||
// @ts-ignore
|
||||
const hasResponse = clineMessages.some(
|
||||
(m: { type: string; text: string }) =>
|
||||
m.type === "say" && m.text && m.text.toLowerCase().includes("cline"),
|
||||
)
|
||||
if (hasResponse) {
|
||||
console.log('Found response containing "Cline" in cline messages')
|
||||
console.log('Found response containing "Roo Code" in cline messages')
|
||||
responseReceived = true
|
||||
break
|
||||
}
|
||||
|
|
@ -327,7 +322,7 @@ suite("Roo Cline Extension Test Suite", () => {
|
|||
if (!responseReceived) {
|
||||
console.log("Final provider state:", await provider.getState())
|
||||
console.log("Final cline messages:", provider.cline?.clineMessages)
|
||||
throw new Error('Did not receive expected response containing "Cline"')
|
||||
throw new Error('Did not receive expected response containing "Code"')
|
||||
}
|
||||
} finally {
|
||||
panel.dispose()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue