feat: AI-Powered Quick Fix Integration

- Implemented a new Quick Fix command in commands.ts and package.json
- Added the collapseFileExcludeSelectRange function for code analysis
- Registered the Quick Fix provider to suggest fixes via AI analysis
This commit is contained in:
bobo.yang 2024-04-17 19:42:56 +08:00
parent af730793bd
commit d50aa4b1b4
5 changed files with 180 additions and 1 deletions

View File

@ -734,6 +734,11 @@
"command": "DevChat.codecomplete_callback",
"title": "Codecomplete Callback",
"category": "DevChat"
},
{
"command": "DevChat.quickFix",
"title": "DevChat Quick Fix",
"category": "DevChat"
}
],
"keybindings": [
@ -788,6 +793,10 @@
{
"command": "DevChat.Chat",
"when": "false"
},
{
"command": "DevChat.quickFix",
"when": "false"
}
],
"explorer/context": [

View File

@ -69,3 +69,88 @@ export async function collapseFile(
return lines.join("\n");
}
export async function collapseFileExculdeSelectRange(
filepath: string,
contents: string,
startRow: number,
endRow: number) : Promise< string > {
const ast = await getAst(filepath, contents);
if (!ast) {
return "";
}
const functionRanges = await findFunctionRanges(filepath, ast.rootNode);
return await collapseAllCodeBlockExculdeSelectRange(functionRanges, filepath, contents, startRow, endRow);
}
export async function collapseAllCodeBlockExculdeSelectRange(
functions: FunctionRange[],
filepath: string,
contents: string,
startRow: number,
endRow: number) {
const commentPrefix = await getCommentPrefix(filepath);
const lines = contents.split("\n");
let disableCollapseRanges: FunctionRange[] = [];
for (const func of functions) {
if (func.define.start.row > endRow || func.define.end.row < startRow) {
continue;
}
disableCollapseRanges.push(func);
}
// visit functions in reverse order
for (const func of functions.reverse()) {
const funcDefine = func.define;
const funcBody = func.body;
if (funcBody.start === funcBody.end) {
continue;
}
if (func.name === "__init__" || func.name === "constructor") {
continue;
}
let bodyStartLine = funcBody.start.row;
let bodyEndLine = funcBody.end.row;
if (funcDefine.start.row === funcBody.start.row) {
bodyStartLine = funcBody.start.row + 1;
bodyEndLine = funcBody.end.row - 1;
}
// whether line before body start column is empty
const lineBeforeBodyStart = lines[funcBody.start.row].slice(0, funcBody.start.column);
if (lineBeforeBodyStart.trim() !== "") {
bodyStartLine = funcBody.start.row + 1;
bodyEndLine = funcBody.end.row - 1;
}
if (bodyEndLine - bodyStartLine <= 3) {
continue;
}
let inDisableRange = false;
for (const disableRange of disableCollapseRanges) {
if (funcDefine === disableRange.define) {
inDisableRange = true;
break;
}
}
if (inDisableRange) {
continue;
}
// replace lines from bodyStartLine to bodyEndLine with "..."
// 获取bodyStartLine这一行的缩进字符需要在"..."之前添加对应的缩进
let indent = lines[bodyStartLine].search(/\S/);
if (indent === -1) {
indent = lines[bodyStartLine].length;
}
const indentStr = " ".repeat(indent);
lines.splice(bodyStartLine, bodyEndLine - bodyStartLine + 1, `${indentStr}${commentPrefix}Code omitted...`);
}
return lines.join("\n");
}

View File

@ -422,3 +422,25 @@ export function registerFixCommand(context: vscode.ExtensionContext) {
vscode.commands.registerCommand("devchat.fix_chinese", callback)
);
}
export function registerQuickFixCommand(context: vscode.ExtensionContext) {
let disposable = vscode.commands.registerCommand(
"DevChat.quickFix",
async (diagnosticMessage: string, code: string, surroundingCode: string) => {
ensureChatPanel(context);
if (!ExtensionContextHolder.provider?.view()) {
// wait 2 seconds
await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(true);
}, 2000);
});
}
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}`;
chatWithDevChat(ExtensionContextHolder.provider?.view()!, prompt);
}
);
context.subscriptions.push(disposable);
}

View File

@ -0,0 +1,60 @@
import * as vscode from "vscode";
import { collapseFileExculdeSelectRange } from "./codecomplete/ast/collapseBlock";
class DevChatQuickFixProvider implements vscode.CodeActionProvider {
public static readonly providedCodeActionKinds = [
vscode.CodeActionKind.QuickFix,
];
provideCodeActions(
document: vscode.TextDocument,
range: vscode.Range | vscode.Selection,
context: vscode.CodeActionContext,
token: vscode.CancellationToken,
): vscode.ProviderResult<(vscode.Command | vscode.CodeAction)[]> {
if (context.diagnostics.length === 0) {
return [];
}
const diagnostic = context.diagnostics[0];
const quickFix = new vscode.CodeAction(
"Ask DevChat",
vscode.CodeActionKind.QuickFix,
);
quickFix.isPreferred = false;
return new Promise(async (resolve) => {
const code = await collapseFileExculdeSelectRange(document.uri.fsPath, document.getText(), range.start.line, range.end.line);
const surroundingRange = new vscode.Range(
Math.max(0, range.start.line - 3),
0,
Math.min(document.lineCount, range.end.line + 3),
0,
);
quickFix.command = {
command: "DevChat.quickFix",
title: "DevChat Quick Fix",
arguments: [
diagnostic.message,
code,
document.getText(surroundingRange)
],
};
resolve([quickFix]);
});
}
}
export default function registerQuickFixProvider() {
vscode.languages.registerCodeActionsProvider(
{ language: "*" },
new DevChatQuickFixProvider(),
{
providedCodeActionKinds: DevChatQuickFixProvider.providedCodeActionKinds,
},
);
}

View File

@ -16,7 +16,7 @@ import {
registerCommentCommand,
registerFixCommand,
registerExplainCommand,
registerQuickFixCommand,
} from './contributes/commands';
import { regLanguageContext } from './contributes/context';
import { regDevChatView } from './contributes/views';
@ -33,6 +33,7 @@ import { stopDevChatBase } from './handler/sendMessageBase';
import { DevChatConfig } from './util/config';
import { InlineCompletionProvider, registerCodeCompleteCallbackCommand } from "./contributes/codecomplete/codecomplete";
import { indexDir } from "./contributes/codecomplete/astIndex";
import registerQuickFixProvider from "./contributes/quickFixProvider";
async function migrateConfig() {
@ -159,10 +160,12 @@ async function activate(context: vscode.ExtensionContext) {
registerDevChatChatCommand(context);
registerCodeLensRangeCommand(context);
registerCodeLensProvider(context);
registerQuickFixCommand(context);
startRpcServer();
logger.channel()?.info(`registerHandleUri:`);
registerHandleUri(context);
registerQuickFixProvider();
const workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath();
if (workspaceDir) {