Browse Source

fix comment on pr

sam hoang 1 year ago
parent
commit
149e86ed0a
5 changed files with 428 additions and 433 deletions
  1. 94 94
      package.json
  2. 167 169
      src/core/CodeActionProvider.ts
  3. 145 143
      src/core/__tests__/CodeActionProvider.test.ts
  4. 3 3
      src/extension.ts
  5. 19 24
      src/test/extension.test.ts

+ 94 - 94
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",

+ 167 - 169
src/core/CodeActionProvider.ts

@@ -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,
-    ];
-
-    // 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 };
-            }
-
-            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)
-            );
-
-            return {
-                range: effectiveRange,
-                text: document.getText(effectiveRange)
-            };
-        } catch (error) {
-            console.error('Error getting effective range:', error);
-            return null;
-        }
-    }
-
-    private getFilePath(document: vscode.TextDocument): string {
-        // Check cache first
-        let filePath = this.filePathCache.get(document);
-        if (filePath) {
-            return filePath;
-        }
-
-        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;
-            }
-
-            // Cache the result
-            this.filePathCache.set(document, filePath);
-            return filePath;
-        } catch (error) {
-            console.error('Error getting file path:', error);
-            return document.uri.fsPath;
-        }
-    }
-
-    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 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 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)
-        );
-    }
-
-    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 [];
-            }
-
-            const filePath = this.getFilePath(document);
-            const actions: vscode.CodeAction[] = [];
-
-            // Create actions using helper method
-            actions.push(this.createAction(
-                ACTION_NAMES.EXPLAIN,
-                vscode.CodeActionKind.QuickFix,
-                COMMAND_IDS.EXPLAIN,
-                [filePath, effectiveRange.text]
-            ));
-
-            // Only process diagnostics if they exist
-            if (context.diagnostics.length > 0) {
-                const relevantDiagnostics = context.diagnostics.filter(d =>
-                    this.hasIntersectingRange(effectiveRange.range, d.range)
-                );
-
-                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]
-                    ));
-                }
-            }
-
-            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 [];
-        }
-    }
-}
+	public static readonly providedCodeActionKinds = [
+		vscode.CodeActionKind.QuickFix,
+		vscode.CodeActionKind.RefactorRewrite,
+	]
+
+	// 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 }
+			}
+
+			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),
+			)
+
+			return {
+				range: effectiveRange,
+				text: document.getText(effectiveRange),
+			}
+		} catch (error) {
+			console.error("Error getting effective range:", error)
+			return null
+		}
+	}
+
+	private getFilePath(document: vscode.TextDocument): string {
+		// Check cache first
+		let filePath = this.filePathCache.get(document)
+		if (filePath) {
+			return filePath
+		}
+
+		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
+			}
+
+			// Cache the result
+			this.filePathCache.set(document, filePath)
+			return filePath
+		} catch (error) {
+			console.error("Error getting file path:", error)
+			return document.uri.fsPath
+		}
+	}
+
+	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 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 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)
+		)
+	}
+
+	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 []
+			}
+
+			const filePath = this.getFilePath(document)
+			const actions: vscode.CodeAction[] = []
+
+			// Create actions using helper method
+			actions.push(
+				this.createAction(ACTION_NAMES.EXPLAIN, vscode.CodeActionKind.QuickFix, COMMAND_IDS.EXPLAIN, [
+					filePath,
+					effectiveRange.text,
+				]),
+			)
+
+			// Only process diagnostics if they exist
+			if (context.diagnostics.length > 0) {
+				const relevantDiagnostics = context.diagnostics.filter((d) =>
+					this.hasIntersectingRange(effectiveRange.range, d.range),
+				)
+
+				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,
+						]),
+					)
+				}
+			}
+
+			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 []
+		}
+	}
+}

+ 145 - 143
src/core/__tests__/CodeActionProvider.test.ts

@@ -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
-    }
-}));
-
-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' }
-        };
-
-        // Mock range
-        mockRange = new vscode.Range(0, 0, 0, 10);
-
-        // Mock context
-        mockContext = {
-            diagnostics: []
-        };
-    });
-
-    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'
-            });
-        });
-
-        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('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 Cline: Explain Code');
-            expect((actions as any)[1].title).toBe('Roo Cline: 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 Cline: 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();
-        });
-    });
-});
+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
+
+	beforeEach(() => {
+		provider = new CodeActionProvider()
+
+		// Mock document
+		mockDocument = {
+			getText: jest.fn(),
+			lineAt: jest.fn(),
+			lineCount: 10,
+			uri: { fsPath: "/test/file.ts" },
+		}
+
+		// Mock range
+		mockRange = new vscode.Range(0, 0, 0, 10)
+
+		// Mock context
+		mockContext = {
+			diagnostics: [],
+		}
+	})
+
+	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",
+			})
+		})
+
+		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("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()
+		})
+	})
+})

+ 3 - 3
src/extension.ts

@@ -201,7 +201,7 @@ export function activate(context: vscode.ExtensionContext) {
 		context,
 		"roo-cline.explainCode",
 		"EXPLAIN",
-		"Any specific questions about this code?",
+		"What would you like Roo to explain?",
 		"E.g. How does the error handling work?",
 	)
 
@@ -209,7 +209,7 @@ export function activate(context: vscode.ExtensionContext) {
 		context,
 		"roo-cline.fixCode",
 		"FIX",
-		"Any specific concerns about fixing this code?",
+		"What would you like Roo to fix?",
 		"E.g. Maintain backward compatibility",
 	)
 
@@ -217,7 +217,7 @@ export function activate(context: vscode.ExtensionContext) {
 		context,
 		"roo-cline.improveCode",
 		"IMPROVE",
-		"Any specific aspects you want to improve?",
+		"What would you like Roo to improve?",
 		"E.g. Focus on performance optimization",
 	)
 

+ 19 - 24
src/test/extension.test.ts

@@ -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