Skip to content

Commit bedd6fd

Browse files
authored
Merge pull request RooCodeInc#656 from samhvw8/feat/add-to-context-code-action
Feat add to context code action & Fix some code action error
2 parents 81a5e80 + 7614048 commit bedd6fd

File tree

10 files changed

+370
-199
lines changed

10 files changed

+370
-199
lines changed

package.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@
118118
"command": "roo-cline.improveCode",
119119
"title": "Roo Code: Improve Code",
120120
"category": "Roo Code"
121+
},
122+
{
123+
"command": "roo-cline.addToContext",
124+
"title": "Roo Code: Add To Context",
125+
"category": "Roo Code"
121126
}
122127
],
123128
"menus": {
@@ -136,6 +141,11 @@
136141
"command": "roo-cline.improveCode",
137142
"when": "editorHasSelection",
138143
"group": "Roo Code@3"
144+
},
145+
{
146+
"command": "roo-cline.addToContext",
147+
"when": "editorHasSelection",
148+
"group": "Roo Code@4"
139149
}
140150
],
141151
"view/title": [

src/core/CodeActionProvider.ts

Lines changed: 24 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,27 @@
11
import * as vscode from "vscode"
2-
import * as path from "path"
3-
import { ClineProvider } from "./webview/ClineProvider"
2+
import { EditorUtils } from "./EditorUtils"
43

54
export const ACTION_NAMES = {
65
EXPLAIN: "Roo Code: Explain Code",
76
FIX: "Roo Code: Fix Code",
7+
FIX_LOGIC: "Roo Code: Fix Logic",
88
IMPROVE: "Roo Code: Improve Code",
9+
ADD_TO_CONTEXT: "Roo Code: Add to Context",
910
} as const
1011

1112
const COMMAND_IDS = {
1213
EXPLAIN: "roo-cline.explainCode",
1314
FIX: "roo-cline.fixCode",
1415
IMPROVE: "roo-cline.improveCode",
16+
ADD_TO_CONTEXT: "roo-cline.addToContext",
1517
} as const
1618

17-
interface DiagnosticData {
18-
message: string
19-
severity: vscode.DiagnosticSeverity
20-
code?: string | number | { value: string | number; target: vscode.Uri }
21-
source?: string
22-
range: vscode.Range
23-
}
24-
25-
interface EffectiveRange {
26-
range: vscode.Range
27-
text: string
28-
}
29-
3019
export class CodeActionProvider implements vscode.CodeActionProvider {
3120
public static readonly providedCodeActionKinds = [
3221
vscode.CodeActionKind.QuickFix,
3322
vscode.CodeActionKind.RefactorRewrite,
3423
]
3524

36-
// Cache file paths for performance
37-
private readonly filePathCache = new WeakMap<vscode.TextDocument, string>()
38-
39-
private getEffectiveRange(
40-
document: vscode.TextDocument,
41-
range: vscode.Range | vscode.Selection,
42-
): EffectiveRange | null {
43-
try {
44-
const selectedText = document.getText(range)
45-
if (selectedText) {
46-
return { range, text: selectedText }
47-
}
48-
49-
const currentLine = document.lineAt(range.start.line)
50-
if (!currentLine.text.trim()) {
51-
return null
52-
}
53-
54-
// Optimize range creation by checking bounds first
55-
const startLine = Math.max(0, currentLine.lineNumber - 1)
56-
const endLine = Math.min(document.lineCount - 1, currentLine.lineNumber + 1)
57-
58-
// Only create new positions if needed
59-
const effectiveRange = new vscode.Range(
60-
startLine === currentLine.lineNumber ? range.start : new vscode.Position(startLine, 0),
61-
endLine === currentLine.lineNumber
62-
? range.end
63-
: new vscode.Position(endLine, document.lineAt(endLine).text.length),
64-
)
65-
66-
return {
67-
range: effectiveRange,
68-
text: document.getText(effectiveRange),
69-
}
70-
} catch (error) {
71-
console.error("Error getting effective range:", error)
72-
return null
73-
}
74-
}
75-
76-
private getFilePath(document: vscode.TextDocument): string {
77-
// Check cache first
78-
let filePath = this.filePathCache.get(document)
79-
if (filePath) {
80-
return filePath
81-
}
82-
83-
try {
84-
const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri)
85-
if (!workspaceFolder) {
86-
filePath = document.uri.fsPath
87-
} else {
88-
const relativePath = path.relative(workspaceFolder.uri.fsPath, document.uri.fsPath)
89-
filePath = !relativePath || relativePath.startsWith("..") ? document.uri.fsPath : relativePath
90-
}
91-
92-
// Cache the result
93-
this.filePathCache.set(document, filePath)
94-
return filePath
95-
} catch (error) {
96-
console.error("Error getting file path:", error)
97-
return document.uri.fsPath
98-
}
99-
}
100-
101-
private createDiagnosticData(diagnostic: vscode.Diagnostic): DiagnosticData {
102-
return {
103-
message: diagnostic.message,
104-
severity: diagnostic.severity,
105-
code: diagnostic.code,
106-
source: diagnostic.source,
107-
range: diagnostic.range, // Reuse the range object
108-
}
109-
}
110-
11125
private createAction(title: string, kind: vscode.CodeActionKind, command: string, args: any[]): vscode.CodeAction {
11226
const action = new vscode.CodeAction(title, kind)
11327
action.command = { command, title, arguments: args }
@@ -126,47 +40,34 @@ export class CodeActionProvider implements vscode.CodeActionProvider {
12640
]
12741
}
12842

129-
private hasIntersectingRange(range1: vscode.Range, range2: vscode.Range): boolean {
130-
// Optimize range intersection check
131-
return !(
132-
range2.end.line < range1.start.line ||
133-
range2.start.line > range1.end.line ||
134-
(range2.end.line === range1.start.line && range2.end.character < range1.start.character) ||
135-
(range2.start.line === range1.end.line && range2.start.character > range1.end.character)
136-
)
137-
}
138-
13943
public provideCodeActions(
14044
document: vscode.TextDocument,
14145
range: vscode.Range | vscode.Selection,
14246
context: vscode.CodeActionContext,
14347
): vscode.ProviderResult<(vscode.CodeAction | vscode.Command)[]> {
14448
try {
145-
const effectiveRange = this.getEffectiveRange(document, range)
49+
const effectiveRange = EditorUtils.getEffectiveRange(document, range)
14650
if (!effectiveRange) {
14751
return []
14852
}
14953

150-
const filePath = this.getFilePath(document)
54+
const filePath = EditorUtils.getFilePath(document)
15155
const actions: vscode.CodeAction[] = []
15256

153-
// Create actions using helper method
154-
// Add explain actions
15557
actions.push(
15658
...this.createActionPair(ACTION_NAMES.EXPLAIN, vscode.CodeActionKind.QuickFix, COMMAND_IDS.EXPLAIN, [
15759
filePath,
15860
effectiveRange.text,
15961
]),
16062
)
16163

162-
// Only process diagnostics if they exist
16364
if (context.diagnostics.length > 0) {
16465
const relevantDiagnostics = context.diagnostics.filter((d) =>
165-
this.hasIntersectingRange(effectiveRange.range, d.range),
66+
EditorUtils.hasIntersectingRange(effectiveRange.range, d.range),
16667
)
16768

16869
if (relevantDiagnostics.length > 0) {
169-
const diagnosticMessages = relevantDiagnostics.map(this.createDiagnosticData)
70+
const diagnosticMessages = relevantDiagnostics.map(EditorUtils.createDiagnosticData)
17071
actions.push(
17172
...this.createActionPair(ACTION_NAMES.FIX, vscode.CodeActionKind.QuickFix, COMMAND_IDS.FIX, [
17273
filePath,
@@ -175,9 +76,15 @@ export class CodeActionProvider implements vscode.CodeActionProvider {
17576
]),
17677
)
17778
}
79+
} else {
80+
actions.push(
81+
...this.createActionPair(ACTION_NAMES.FIX_LOGIC, vscode.CodeActionKind.QuickFix, COMMAND_IDS.FIX, [
82+
filePath,
83+
effectiveRange.text,
84+
]),
85+
)
17886
}
17987

180-
// Add improve actions
18188
actions.push(
18289
...this.createActionPair(
18390
ACTION_NAMES.IMPROVE,
@@ -187,6 +94,15 @@ export class CodeActionProvider implements vscode.CodeActionProvider {
18794
),
18895
)
18996

97+
actions.push(
98+
this.createAction(
99+
ACTION_NAMES.ADD_TO_CONTEXT,
100+
vscode.CodeActionKind.QuickFix,
101+
COMMAND_IDS.ADD_TO_CONTEXT,
102+
[filePath, effectiveRange.text],
103+
),
104+
)
105+
190106
return actions
191107
} catch (error) {
192108
console.error("Error providing code actions:", error)

src/core/EditorUtils.ts

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import * as vscode from "vscode"
2+
import * as path from "path"
3+
4+
export interface EffectiveRange {
5+
range: vscode.Range
6+
text: string
7+
}
8+
9+
export interface DiagnosticData {
10+
message: string
11+
severity: vscode.DiagnosticSeverity
12+
code?: string | number | { value: string | number; target: vscode.Uri }
13+
source?: string
14+
range: vscode.Range
15+
}
16+
17+
export interface EditorContext {
18+
filePath: string
19+
selectedText: string
20+
diagnostics?: DiagnosticData[]
21+
}
22+
23+
export class EditorUtils {
24+
// Cache file paths for performance
25+
private static readonly filePathCache = new WeakMap<vscode.TextDocument, string>()
26+
27+
static getEffectiveRange(
28+
document: vscode.TextDocument,
29+
range: vscode.Range | vscode.Selection,
30+
): EffectiveRange | null {
31+
try {
32+
const selectedText = document.getText(range)
33+
if (selectedText) {
34+
return { range, text: selectedText }
35+
}
36+
37+
const currentLine = document.lineAt(range.start.line)
38+
if (!currentLine.text.trim()) {
39+
return null
40+
}
41+
42+
// Optimize range creation by checking bounds first
43+
const startLine = Math.max(0, currentLine.lineNumber - 1)
44+
const endLine = Math.min(document.lineCount - 1, currentLine.lineNumber + 1)
45+
46+
// Only create new positions if needed
47+
const effectiveRange = new vscode.Range(
48+
startLine === currentLine.lineNumber ? range.start : new vscode.Position(startLine, 0),
49+
endLine === currentLine.lineNumber
50+
? range.end
51+
: new vscode.Position(endLine, document.lineAt(endLine).text.length),
52+
)
53+
54+
return {
55+
range: effectiveRange,
56+
text: document.getText(effectiveRange),
57+
}
58+
} catch (error) {
59+
console.error("Error getting effective range:", error)
60+
return null
61+
}
62+
}
63+
64+
static getFilePath(document: vscode.TextDocument): string {
65+
// Check cache first
66+
let filePath = this.filePathCache.get(document)
67+
if (filePath) {
68+
return filePath
69+
}
70+
71+
try {
72+
const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri)
73+
if (!workspaceFolder) {
74+
filePath = document.uri.fsPath
75+
} else {
76+
const relativePath = path.relative(workspaceFolder.uri.fsPath, document.uri.fsPath)
77+
filePath = !relativePath || relativePath.startsWith("..") ? document.uri.fsPath : relativePath
78+
}
79+
80+
// Cache the result
81+
this.filePathCache.set(document, filePath)
82+
return filePath
83+
} catch (error) {
84+
console.error("Error getting file path:", error)
85+
return document.uri.fsPath
86+
}
87+
}
88+
89+
static createDiagnosticData(diagnostic: vscode.Diagnostic): DiagnosticData {
90+
return {
91+
message: diagnostic.message,
92+
severity: diagnostic.severity,
93+
code: diagnostic.code,
94+
source: diagnostic.source,
95+
range: diagnostic.range,
96+
}
97+
}
98+
99+
static hasIntersectingRange(range1: vscode.Range, range2: vscode.Range): boolean {
100+
return !(
101+
range2.end.line < range1.start.line ||
102+
range2.start.line > range1.end.line ||
103+
(range2.end.line === range1.start.line && range2.end.character < range1.start.character) ||
104+
(range2.start.line === range1.end.line && range2.start.character > range1.end.character)
105+
)
106+
}
107+
108+
static getEditorContext(editor?: vscode.TextEditor): EditorContext | null {
109+
try {
110+
if (!editor) {
111+
editor = vscode.window.activeTextEditor
112+
}
113+
if (!editor) {
114+
return null
115+
}
116+
117+
const document = editor.document
118+
const selection = editor.selection
119+
const effectiveRange = this.getEffectiveRange(document, selection)
120+
121+
if (!effectiveRange) {
122+
return null
123+
}
124+
125+
const filePath = this.getFilePath(document)
126+
const diagnostics = vscode.languages
127+
.getDiagnostics(document.uri)
128+
.filter((d) => this.hasIntersectingRange(effectiveRange.range, d.range))
129+
.map(this.createDiagnosticData)
130+
131+
return {
132+
filePath,
133+
selectedText: effectiveRange.text,
134+
...(diagnostics.length > 0 ? { diagnostics } : {}),
135+
}
136+
} catch (error) {
137+
console.error("Error getting editor context:", error)
138+
return null
139+
}
140+
}
141+
}

0 commit comments

Comments
 (0)