mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 04:11:10 -05:00
refactor: centralize editor utilities and unify command handling
- Create EditorUtils class to centralize shared editor functionality - Remove duplicated code between CodeActionProvider and command handlers - Improve command handling to work consistently for both code actions and direct commands - Add better type safety and error handling for editor operations
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import * as vscode from "vscode"
|
import * as vscode from "vscode"
|
||||||
import * as path from "path"
|
|
||||||
import { ClineProvider } from "./webview/ClineProvider"
|
import { ClineProvider } from "./webview/ClineProvider"
|
||||||
|
import { EditorUtils } from "./EditorUtils"
|
||||||
|
|
||||||
export const ACTION_NAMES = {
|
export const ACTION_NAMES = {
|
||||||
EXPLAIN: "Roo Code: Explain Code",
|
EXPLAIN: "Roo Code: Explain Code",
|
||||||
@@ -14,100 +14,12 @@ const COMMAND_IDS = {
|
|||||||
IMPROVE: "roo-cline.improveCode",
|
IMPROVE: "roo-cline.improveCode",
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
interface DiagnosticData {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CodeActionProvider implements vscode.CodeActionProvider {
|
export class CodeActionProvider implements vscode.CodeActionProvider {
|
||||||
public static readonly providedCodeActionKinds = [
|
public static readonly providedCodeActionKinds = [
|
||||||
vscode.CodeActionKind.QuickFix,
|
vscode.CodeActionKind.QuickFix,
|
||||||
vscode.CodeActionKind.RefactorRewrite,
|
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 {
|
private createAction(title: string, kind: vscode.CodeActionKind, command: string, args: any[]): vscode.CodeAction {
|
||||||
const action = new vscode.CodeAction(title, kind)
|
const action = new vscode.CodeAction(title, kind)
|
||||||
action.command = { command, title, arguments: args }
|
action.command = { command, title, arguments: args }
|
||||||
@@ -126,32 +38,20 @@ export class CodeActionProvider implements vscode.CodeActionProvider {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
public provideCodeActions(
|
||||||
document: vscode.TextDocument,
|
document: vscode.TextDocument,
|
||||||
range: vscode.Range | vscode.Selection,
|
range: vscode.Range | vscode.Selection,
|
||||||
context: vscode.CodeActionContext,
|
context: vscode.CodeActionContext,
|
||||||
): vscode.ProviderResult<(vscode.CodeAction | vscode.Command)[]> {
|
): vscode.ProviderResult<(vscode.CodeAction | vscode.Command)[]> {
|
||||||
try {
|
try {
|
||||||
const effectiveRange = this.getEffectiveRange(document, range)
|
const effectiveRange = EditorUtils.getEffectiveRange(document, range)
|
||||||
if (!effectiveRange) {
|
if (!effectiveRange) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
const filePath = this.getFilePath(document)
|
const filePath = EditorUtils.getFilePath(document)
|
||||||
const actions: vscode.CodeAction[] = []
|
const actions: vscode.CodeAction[] = []
|
||||||
|
|
||||||
// Create actions using helper method
|
|
||||||
// Add explain actions
|
|
||||||
actions.push(
|
actions.push(
|
||||||
...this.createActionPair(ACTION_NAMES.EXPLAIN, vscode.CodeActionKind.QuickFix, COMMAND_IDS.EXPLAIN, [
|
...this.createActionPair(ACTION_NAMES.EXPLAIN, vscode.CodeActionKind.QuickFix, COMMAND_IDS.EXPLAIN, [
|
||||||
filePath,
|
filePath,
|
||||||
@@ -159,14 +59,13 @@ export class CodeActionProvider implements vscode.CodeActionProvider {
|
|||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Only process diagnostics if they exist
|
|
||||||
if (context.diagnostics.length > 0) {
|
if (context.diagnostics.length > 0) {
|
||||||
const relevantDiagnostics = context.diagnostics.filter((d) =>
|
const relevantDiagnostics = context.diagnostics.filter((d) =>
|
||||||
this.hasIntersectingRange(effectiveRange.range, d.range),
|
EditorUtils.hasIntersectingRange(effectiveRange.range, d.range),
|
||||||
)
|
)
|
||||||
|
|
||||||
if (relevantDiagnostics.length > 0) {
|
if (relevantDiagnostics.length > 0) {
|
||||||
const diagnosticMessages = relevantDiagnostics.map(this.createDiagnosticData)
|
const diagnosticMessages = relevantDiagnostics.map(EditorUtils.createDiagnosticData)
|
||||||
actions.push(
|
actions.push(
|
||||||
...this.createActionPair(ACTION_NAMES.FIX, vscode.CodeActionKind.QuickFix, COMMAND_IDS.FIX, [
|
...this.createActionPair(ACTION_NAMES.FIX, vscode.CodeActionKind.QuickFix, COMMAND_IDS.FIX, [
|
||||||
filePath,
|
filePath,
|
||||||
@@ -177,7 +76,6 @@ export class CodeActionProvider implements vscode.CodeActionProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add improve actions
|
|
||||||
actions.push(
|
actions.push(
|
||||||
...this.createActionPair(
|
...this.createActionPair(
|
||||||
ACTION_NAMES.IMPROVE,
|
ACTION_NAMES.IMPROVE,
|
||||||
|
|||||||
141
src/core/EditorUtils.ts
Normal file
141
src/core/EditorUtils.ts
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
import * as vscode from "vscode"
|
||||||
|
import * as path from "path"
|
||||||
|
|
||||||
|
export interface EffectiveRange {
|
||||||
|
range: vscode.Range
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DiagnosticData {
|
||||||
|
message: string
|
||||||
|
severity: vscode.DiagnosticSeverity
|
||||||
|
code?: string | number | { value: string | number; target: vscode.Uri }
|
||||||
|
source?: string
|
||||||
|
range: vscode.Range
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EditorContext {
|
||||||
|
filePath: string
|
||||||
|
selectedText: string
|
||||||
|
diagnostics?: DiagnosticData[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EditorUtils {
|
||||||
|
// Cache file paths for performance
|
||||||
|
private static readonly filePathCache = new WeakMap<vscode.TextDocument, string>()
|
||||||
|
|
||||||
|
static 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static createDiagnosticData(diagnostic: vscode.Diagnostic): DiagnosticData {
|
||||||
|
return {
|
||||||
|
message: diagnostic.message,
|
||||||
|
severity: diagnostic.severity,
|
||||||
|
code: diagnostic.code,
|
||||||
|
source: diagnostic.source,
|
||||||
|
range: diagnostic.range,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static hasIntersectingRange(range1: vscode.Range, range2: vscode.Range): boolean {
|
||||||
|
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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
static getEditorContext(editor?: vscode.TextEditor): EditorContext | null {
|
||||||
|
try {
|
||||||
|
if (!editor) {
|
||||||
|
editor = vscode.window.activeTextEditor
|
||||||
|
}
|
||||||
|
if (!editor) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const document = editor.document
|
||||||
|
const selection = editor.selection
|
||||||
|
const effectiveRange = this.getEffectiveRange(document, selection)
|
||||||
|
|
||||||
|
if (!effectiveRange) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const filePath = this.getFilePath(document)
|
||||||
|
const diagnostics = vscode.languages
|
||||||
|
.getDiagnostics(document.uri)
|
||||||
|
.filter((d) => this.hasIntersectingRange(effectiveRange.range, d.range))
|
||||||
|
.map(this.createDiagnosticData)
|
||||||
|
|
||||||
|
return {
|
||||||
|
filePath,
|
||||||
|
selectedText: effectiveRange.text,
|
||||||
|
...(diagnostics.length > 0 ? { diagnostics } : {}),
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error getting editor context:", error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import { ClineProvider } from "./core/webview/ClineProvider"
|
|||||||
import { createClineAPI } from "./exports"
|
import { createClineAPI } from "./exports"
|
||||||
import "./utils/path" // necessary to have access to String.prototype.toPosix
|
import "./utils/path" // necessary to have access to String.prototype.toPosix
|
||||||
import { ACTION_NAMES, CodeActionProvider } from "./core/CodeActionProvider"
|
import { ACTION_NAMES, CodeActionProvider } from "./core/CodeActionProvider"
|
||||||
|
import { EditorUtils } from "./core/EditorUtils"
|
||||||
import { DIFF_VIEW_URI_SCHEME } from "./integrations/editor/DiffViewProvider"
|
import { DIFF_VIEW_URI_SCHEME } from "./integrations/editor/DiffViewProvider"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -178,9 +179,7 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
let userInput: string | undefined
|
let userInput: string | undefined
|
||||||
|
|
||||||
context.subscriptions.push(
|
context.subscriptions.push(
|
||||||
vscode.commands.registerCommand(
|
vscode.commands.registerCommand(command, async (...args: any[]) => {
|
||||||
command,
|
|
||||||
async (filePath: string, selectedText: string, diagnostics?: any[]) => {
|
|
||||||
if (inputPrompt) {
|
if (inputPrompt) {
|
||||||
userInput = await vscode.window.showInputBox({
|
userInput = await vscode.window.showInputBox({
|
||||||
prompt: inputPrompt,
|
prompt: inputPrompt,
|
||||||
@@ -188,16 +187,29 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle both code action and direct command cases
|
||||||
|
let filePath: string
|
||||||
|
let selectedText: string
|
||||||
|
let diagnostics: any[] | undefined
|
||||||
|
|
||||||
|
if (args.length > 1) {
|
||||||
|
// Called from code action
|
||||||
|
;[filePath, selectedText, diagnostics] = args
|
||||||
|
} else {
|
||||||
|
// Called directly from command palette
|
||||||
|
const context = EditorUtils.getEditorContext()
|
||||||
|
if (!context) return
|
||||||
|
;({ filePath, selectedText, diagnostics } = context)
|
||||||
|
}
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
filePath,
|
...{ filePath, selectedText },
|
||||||
selectedText,
|
|
||||||
...(diagnostics ? { diagnostics } : {}),
|
...(diagnostics ? { diagnostics } : {}),
|
||||||
...(userInput ? { userInput } : {}),
|
...(userInput ? { userInput } : {}),
|
||||||
}
|
}
|
||||||
|
|
||||||
await ClineProvider.handleCodeAction(command, promptType, params)
|
await ClineProvider.handleCodeAction(command, promptType, params)
|
||||||
},
|
}),
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user