Skip to content

Commit 8f1dbce

Browse files
authored
Merge pull request #498 from devchat-ai/feature/ai-powered-quick-fixes-#345
Implement AI-Powered Quick Fix for VSCode Plugin
2 parents af73079 + d50aa4b commit 8f1dbce

File tree

5 files changed

+180
-1
lines changed

5 files changed

+180
-1
lines changed

package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,11 @@
734734
"command": "DevChat.codecomplete_callback",
735735
"title": "Codecomplete Callback",
736736
"category": "DevChat"
737+
},
738+
{
739+
"command": "DevChat.quickFix",
740+
"title": "DevChat Quick Fix",
741+
"category": "DevChat"
737742
}
738743
],
739744
"keybindings": [
@@ -788,6 +793,10 @@
788793
{
789794
"command": "DevChat.Chat",
790795
"when": "false"
796+
},
797+
{
798+
"command": "DevChat.quickFix",
799+
"when": "false"
791800
}
792801
],
793802
"explorer/context": [

src/contributes/codecomplete/ast/collapseBlock.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,88 @@ export async function collapseFile(
6969

7070
return lines.join("\n");
7171
}
72+
73+
74+
export async function collapseFileExculdeSelectRange(
75+
filepath: string,
76+
contents: string,
77+
startRow: number,
78+
endRow: number) : Promise< string > {
79+
const ast = await getAst(filepath, contents);
80+
if (!ast) {
81+
return "";
82+
}
83+
84+
const functionRanges = await findFunctionRanges(filepath, ast.rootNode);
85+
return await collapseAllCodeBlockExculdeSelectRange(functionRanges, filepath, contents, startRow, endRow);
86+
}
87+
88+
export async function collapseAllCodeBlockExculdeSelectRange(
89+
functions: FunctionRange[],
90+
filepath: string,
91+
contents: string,
92+
startRow: number,
93+
endRow: number) {
94+
const commentPrefix = await getCommentPrefix(filepath);
95+
const lines = contents.split("\n");
96+
97+
let disableCollapseRanges: FunctionRange[] = [];
98+
for (const func of functions) {
99+
if (func.define.start.row > endRow || func.define.end.row < startRow) {
100+
continue;
101+
}
102+
103+
disableCollapseRanges.push(func);
104+
}
105+
106+
// visit functions in reverse order
107+
for (const func of functions.reverse()) {
108+
const funcDefine = func.define;
109+
const funcBody = func.body;
110+
111+
if (funcBody.start === funcBody.end) {
112+
continue;
113+
}
114+
if (func.name === "__init__" || func.name === "constructor") {
115+
continue;
116+
}
117+
118+
let bodyStartLine = funcBody.start.row;
119+
let bodyEndLine = funcBody.end.row;
120+
if (funcDefine.start.row === funcBody.start.row) {
121+
bodyStartLine = funcBody.start.row + 1;
122+
bodyEndLine = funcBody.end.row - 1;
123+
}
124+
// whether line before body start column is empty
125+
const lineBeforeBodyStart = lines[funcBody.start.row].slice(0, funcBody.start.column);
126+
if (lineBeforeBodyStart.trim() !== "") {
127+
bodyStartLine = funcBody.start.row + 1;
128+
bodyEndLine = funcBody.end.row - 1;
129+
}
130+
131+
if (bodyEndLine - bodyStartLine <= 3) {
132+
continue;
133+
}
134+
let inDisableRange = false;
135+
for (const disableRange of disableCollapseRanges) {
136+
if (funcDefine === disableRange.define) {
137+
inDisableRange = true;
138+
break;
139+
}
140+
}
141+
if (inDisableRange) {
142+
continue;
143+
}
144+
145+
// replace lines from bodyStartLine to bodyEndLine with "..."
146+
// 获取bodyStartLine这一行的缩进字符,需要在"..."之前添加对应的缩进
147+
let indent = lines[bodyStartLine].search(/\S/);
148+
if (indent === -1) {
149+
indent = lines[bodyStartLine].length;
150+
}
151+
const indentStr = " ".repeat(indent);
152+
lines.splice(bodyStartLine, bodyEndLine - bodyStartLine + 1, `${indentStr}${commentPrefix}Code omitted...`);
153+
}
154+
155+
return lines.join("\n");
156+
}

src/contributes/commands.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,3 +422,25 @@ export function registerFixCommand(context: vscode.ExtensionContext) {
422422
vscode.commands.registerCommand("devchat.fix_chinese", callback)
423423
);
424424
}
425+
426+
export function registerQuickFixCommand(context: vscode.ExtensionContext) {
427+
let disposable = vscode.commands.registerCommand(
428+
"DevChat.quickFix",
429+
async (diagnosticMessage: string, code: string, surroundingCode: string) => {
430+
ensureChatPanel(context);
431+
if (!ExtensionContextHolder.provider?.view()) {
432+
// wait 2 seconds
433+
await new Promise((resolve, reject) => {
434+
setTimeout(() => {
435+
resolve(true);
436+
}, 2000);
437+
});
438+
}
439+
440+
const prompt = `current edit file is:\n\`\`\`\n${code}\n\`\`\`\n\nThere is an error in the above code:\n\`\`\`\n${surroundingCode}\n\`\`\`\n\nHow do I fix this problem in the above code?: ${diagnosticMessage}`;
441+
chatWithDevChat(ExtensionContextHolder.provider?.view()!, prompt);
442+
}
443+
);
444+
445+
context.subscriptions.push(disposable);
446+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import * as vscode from "vscode";
2+
import { collapseFileExculdeSelectRange } from "./codecomplete/ast/collapseBlock";
3+
4+
class DevChatQuickFixProvider implements vscode.CodeActionProvider {
5+
public static readonly providedCodeActionKinds = [
6+
vscode.CodeActionKind.QuickFix,
7+
];
8+
9+
provideCodeActions(
10+
document: vscode.TextDocument,
11+
range: vscode.Range | vscode.Selection,
12+
context: vscode.CodeActionContext,
13+
token: vscode.CancellationToken,
14+
): vscode.ProviderResult<(vscode.Command | vscode.CodeAction)[]> {
15+
if (context.diagnostics.length === 0) {
16+
return [];
17+
}
18+
19+
const diagnostic = context.diagnostics[0];
20+
const quickFix = new vscode.CodeAction(
21+
"Ask DevChat",
22+
vscode.CodeActionKind.QuickFix,
23+
);
24+
quickFix.isPreferred = false;
25+
26+
return new Promise(async (resolve) => {
27+
const code = await collapseFileExculdeSelectRange(document.uri.fsPath, document.getText(), range.start.line, range.end.line);
28+
29+
const surroundingRange = new vscode.Range(
30+
Math.max(0, range.start.line - 3),
31+
0,
32+
Math.min(document.lineCount, range.end.line + 3),
33+
0,
34+
);
35+
36+
quickFix.command = {
37+
command: "DevChat.quickFix",
38+
title: "DevChat Quick Fix",
39+
arguments: [
40+
diagnostic.message,
41+
code,
42+
document.getText(surroundingRange)
43+
],
44+
};
45+
46+
resolve([quickFix]);
47+
});
48+
}
49+
}
50+
51+
export default function registerQuickFixProvider() {
52+
vscode.languages.registerCodeActionsProvider(
53+
{ language: "*" },
54+
new DevChatQuickFixProvider(),
55+
{
56+
providedCodeActionKinds: DevChatQuickFixProvider.providedCodeActionKinds,
57+
},
58+
);
59+
}
60+

src/extension.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
registerCommentCommand,
1717
registerFixCommand,
1818
registerExplainCommand,
19-
19+
registerQuickFixCommand,
2020
} from './contributes/commands';
2121
import { regLanguageContext } from './contributes/context';
2222
import { regDevChatView } from './contributes/views';
@@ -33,6 +33,7 @@ import { stopDevChatBase } from './handler/sendMessageBase';
3333
import { DevChatConfig } from './util/config';
3434
import { InlineCompletionProvider, registerCodeCompleteCallbackCommand } from "./contributes/codecomplete/codecomplete";
3535
import { indexDir } from "./contributes/codecomplete/astIndex";
36+
import registerQuickFixProvider from "./contributes/quickFixProvider";
3637

3738

3839
async function migrateConfig() {
@@ -159,10 +160,12 @@ async function activate(context: vscode.ExtensionContext) {
159160
registerDevChatChatCommand(context);
160161
registerCodeLensRangeCommand(context);
161162
registerCodeLensProvider(context);
163+
registerQuickFixCommand(context);
162164

163165
startRpcServer();
164166
logger.channel()?.info(`registerHandleUri:`);
165167
registerHandleUri(context);
168+
registerQuickFixProvider();
166169

167170
const workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath();
168171
if (workspaceDir) {

0 commit comments

Comments
 (0)