2024-03-07 18:50:38 +08:00
import * as vscode from "vscode" ;
import * as fs from "fs" ;
import * as path from "path" ;
import * as util from "util" ;
import { sendFileSelectMessage , sendCodeSelectMessage } from "./util" ;
import { ExtensionContextHolder } from "../util/extensionContext" ;
import { FilePairManager } from "../util/diffFilePairs" ;
import { UiUtilWrapper } from "../util/uiUtil" ;
2024-04-01 17:15:44 +08:00
import { sendCommandListByDevChatRun } from '../handler/workflowCommandHandler' ;
2024-07-08 19:33:08 +08:00
import { DevChatClient } from "../toolwrapper/devchatClient" ;
2024-04-01 17:15:44 +08:00
import { chatWithDevChat } from '../handler/chatHandler' ;
import { focusDevChatInput } from '../handler/focusHandler' ;
import { DevChatConfig } from '../util/config' ;
2024-06-07 17:22:07 +08:00
import { MessageHandler } from "../handler/messageHandler" ;
2024-07-16 07:55:02 +08:00
import { startLocalService } from '../util/localService' ;
import { logger } from "../util/logger" ;
2023-05-10 19:25:11 +08:00
2023-12-20 12:57:55 +08:00
const readdir = util . promisify ( fs . readdir ) ;
const mkdir = util . promisify ( fs . mkdir ) ;
const copyFile = util . promisify ( fs . copyFile ) ;
2024-06-17 21:33:30 +08:00
// It is used to copy workflow commands to user directory.
2023-12-20 12:57:55 +08:00
async function copyDirectory ( src : string , dest : string ) : Promise < void > {
2024-03-07 18:50:38 +08:00
await mkdir ( dest , { recursive : true } ) ;
const entries = await readdir ( src , { withFileTypes : true } ) ;
2023-12-20 12:57:55 +08:00
2024-03-07 18:50:38 +08:00
for ( let entry of entries ) {
const srcPath = path . join ( src , entry . name ) ;
const destPath = path . join ( dest , entry . name ) ;
2023-12-20 12:57:55 +08:00
2024-11-26 09:42:35 +08:00
if ( entry . name === ".git" ) {
continue ;
}
2024-03-07 18:50:38 +08:00
if ( entry . isDirectory ( ) ) {
await copyDirectory ( srcPath , destPath ) ;
} else {
await copyFile ( srcPath , destPath ) ;
2023-12-20 12:57:55 +08:00
}
2024-03-07 18:50:38 +08:00
}
2023-12-20 12:57:55 +08:00
}
2023-11-29 23:34:15 +08:00
2024-04-01 17:15:44 +08:00
export function registerOpenChatPanelCommand ( context : vscode.ExtensionContext ) {
2024-03-07 18:50:38 +08:00
let disposable = vscode . commands . registerCommand (
"devchat.openChatPanel" ,
async ( ) = > {
await vscode . commands . executeCommand ( "devchat-view.focus" ) ;
await focusDevChatInput ( ExtensionContextHolder . provider ? . view ( ) ! ) ;
}
) ;
context . subscriptions . push ( disposable ) ;
2023-05-05 21:27:40 +08:00
}
2024-03-07 18:50:38 +08:00
async function ensureChatPanel (
context : vscode.ExtensionContext
) : Promise < boolean > {
await vscode . commands . executeCommand ( "devchat-view.focus" ) ;
return true ;
2023-05-05 21:27:40 +08:00
}
2024-04-01 17:15:44 +08:00
export function registerAddContextCommand ( context : vscode.ExtensionContext ) {
2024-03-07 18:50:38 +08:00
const callback = async ( uri : { fsPath : any } ) = > {
if ( ! ( await ensureChatPanel ( context ) ) ) {
return ;
}
await sendFileSelectMessage (
ExtensionContextHolder . provider ? . view ( ) ! ,
uri . fsPath
) ;
} ;
context . subscriptions . push (
vscode . commands . registerCommand ( "devchat.addContext" , callback )
) ;
context . subscriptions . push (
vscode . commands . registerCommand ( "devchat.addConext_chinese" , callback )
) ;
2023-05-05 21:27:40 +08:00
}
2024-04-01 17:15:44 +08:00
export function registerAskForCodeCommand ( context : vscode.ExtensionContext ) {
2024-03-07 18:50:38 +08:00
const callback = async ( ) = > {
const editor = vscode . window . activeTextEditor ;
if ( editor ) {
if ( ! ( await ensureChatPanel ( context ) ) ) {
return ;
}
const selectedText = editor . document . getText ( editor . selection ) ;
await sendCodeSelectMessage (
ExtensionContextHolder . provider ? . view ( ) ! ,
editor . document . fileName ,
selectedText ,
editor . selection . start . line
) ;
}
} ;
context . subscriptions . push (
vscode . commands . registerCommand ( "devchat.askForCode" , callback )
) ;
context . subscriptions . push (
vscode . commands . registerCommand ( "devchat.askForCode_chinese" , callback )
) ;
2023-05-05 21:27:40 +08:00
}
2024-04-01 17:15:44 +08:00
export function registerAskForFileCommand ( context : vscode.ExtensionContext ) {
2024-03-07 18:50:38 +08:00
const callback = async ( ) = > {
const editor = vscode . window . activeTextEditor ;
if ( editor ) {
if ( ! ( await ensureChatPanel ( context ) ) ) {
return ;
}
await sendFileSelectMessage (
ExtensionContextHolder . provider ? . view ( ) ! ,
editor . document . fileName
) ;
}
} ;
context . subscriptions . push (
vscode . commands . registerCommand ( "devchat.askForFile" , callback )
) ;
context . subscriptions . push (
vscode . commands . registerCommand ( "devchat.askForFile_chinese" , callback )
) ;
2023-05-05 21:27:40 +08:00
}
2023-05-31 16:10:53 +08:00
2024-03-07 18:50:38 +08:00
export function registerStatusBarItemClickCommand (
context : vscode.ExtensionContext
) {
context . subscriptions . push (
vscode . commands . registerCommand ( "devcaht.onStatusBarClick" , async ( ) = > {
await vscode . commands . executeCommand ( "devchat-view.focus" ) ;
} )
) ;
2023-05-31 16:10:53 +08:00
}
2023-07-06 07:42:44 +08:00
export function regPythonPathCommand ( context : vscode.ExtensionContext ) {
2024-04-01 17:15:44 +08:00
context . subscriptions . push (
vscode . commands . registerCommand ( "devchat.PythonPath" , async ( ) = > {
const pythonPath = ( await vscode . window . showInputBox ( {
title : "Set Python Path" ,
placeHolder : "Set Python Path" ,
} ) ) ? ? "" ;
if ( pythonPath ) {
2024-04-17 00:07:09 +08:00
DevChatConfig . getInstance ( ) . set ( "python_for_chat" , pythonPath ) ;
2024-04-01 17:15:44 +08:00
}
} )
) ;
2023-07-06 07:42:44 +08:00
}
2023-05-31 16:10:53 +08:00
export function regApplyDiffResultCommand ( context : vscode.ExtensionContext ) {
2024-03-07 18:50:38 +08:00
context . subscriptions . push (
vscode . commands . registerCommand ( "devchat.applyDiffResult" , async ( ) = > {
const activeEditor = vscode . window . activeTextEditor ;
const fileName = activeEditor ! . document . fileName ;
const [ leftUri , rightUri ] = FilePairManager . getInstance ( ) . findPair (
fileName
) || [ undefined , undefined ] ;
if ( leftUri && rightUri ) {
// 获取对比的两个文件
const leftDoc = await vscode . workspace . openTextDocument ( leftUri ) ;
const rightDoc = await vscode . workspace . openTextDocument ( rightUri ) ;
// close rightDoc
await vscode . commands . executeCommand (
"workbench.action.closeActiveEditor"
) ;
// 将右边文档的内容替换到左边文档
const leftEditor = await vscode . window . showTextDocument ( leftDoc ) ;
await leftEditor . edit ( ( editBuilder ) = > {
const fullRange = new vscode . Range ( 0 , 0 , leftDoc . lineCount , 0 ) ;
editBuilder . replace ( fullRange , rightDoc . getText ( ) ) ;
} ) ;
// 保存左边文档
await leftDoc . save ( ) ;
2024-06-07 17:22:07 +08:00
MessageHandler . sendMessage (
ExtensionContextHolder . provider ? . view ( ) ! ,
{ command : 'codeDiffApply' , 'value' : { } }
) ;
2024-03-07 18:50:38 +08:00
} else {
vscode . window . showErrorMessage ( "No file to apply diff result." ) ;
}
} )
) ;
2023-05-31 16:10:53 +08:00
}
2024-11-26 09:42:35 +08:00
function getExtensionVersion ( ) : string {
const packageJsonPath = path . join ( __dirname , '..' , 'package.json' ) ;
const packageJson = JSON . parse ( fs . readFileSync ( packageJsonPath , 'utf8' ) ) ;
return packageJson . version ;
}
2024-03-07 18:50:38 +08:00
export function registerInstallCommandsCommand (
context : vscode.ExtensionContext
) {
let disposable = vscode . commands . registerCommand (
"DevChat.InstallCommands" ,
async ( ) = > {
2024-11-05 11:28:51 +08:00
logger . channel ( ) ? . debug ( "InstallCommands command triggered." ) ;
2024-03-07 18:50:38 +08:00
const homePath = process . env . HOME || process . env . USERPROFILE || "" ;
2024-05-13 12:42:26 +08:00
const sysDirPath = path . join ( homePath , ".chat" , "scripts" ) ;
2024-09-10 11:32:23 +08:00
const sysMericoDirPath = path . join ( homePath , ".chat" , "scripts" , "merico" ) ;
2024-11-26 09:42:35 +08:00
const devchatConfig = DevChatConfig . getInstance ( ) ;
2024-03-07 18:50:38 +08:00
const pluginDirPath = path . join (
UiUtilWrapper . extensionPath ( ) ,
"workflowsCommands"
) ; // Adjust this path as needed
2024-07-08 19:33:08 +08:00
const dcClient = new DevChatClient ( ) ;
2024-11-26 09:42:35 +08:00
const updatePublicWorkflow = devchatConfig . get ( "update_public_workflow" , true ) ;
const currentVersion = UiUtilWrapper . extensionPath ( ) ;
const previousVersion = devchatConfig . get ( "last_devchat_version" , "" ) ;
2024-03-07 18:50:38 +08:00
2024-12-02 14:40:19 +08:00
let copiedDirectory = false ;
2024-11-26 09:42:35 +08:00
if ( ! fs . existsSync ( sysMericoDirPath ) || ( updatePublicWorkflow === false && currentVersion !== previousVersion ) ) {
2024-11-05 11:28:51 +08:00
logger . channel ( ) ? . debug ( "Creating directory: " + sysMericoDirPath ) ;
2024-12-18 10:43:07 +08:00
if ( fs . existsSync ( sysMericoDirPath ) ) {
fs . rmSync ( sysMericoDirPath , { recursive : true , force : true } ) ;
}
2024-03-07 18:50:38 +08:00
await copyDirectory ( pluginDirPath , sysDirPath ) ;
2024-12-02 14:40:19 +08:00
copiedDirectory = true ;
2024-03-07 18:50:38 +08:00
}
2024-11-26 09:42:35 +08:00
devchatConfig . set ( "last_devchat_version" , currentVersion ) ;
2024-03-07 18:50:38 +08:00
2024-12-02 14:40:19 +08:00
if ( copiedDirectory ) {
logger . channel ( ) ? . debug ( "Directory copied successfully." ) ;
2024-03-07 18:50:38 +08:00
sendCommandListByDevChatRun ( ) ;
} else {
2024-12-02 14:40:19 +08:00
// Check if ~/.chat/scripts directory exists
if ( ! fs . existsSync ( sysMericoDirPath ) ) {
// Directory does not exist, wait for updateWorkflows to finish
logger . channel ( ) ? . debug ( "Update workflows..." ) ;
await dcClient . updateWorkflows ( ) ;
await dcClient . updateCustomWorkflows ( ) ;
sendCommandListByDevChatRun ( ) ;
} else {
// Directory exists, execute sendCommandListByDevChatRun immediately
logger . channel ( ) ? . debug ( "Sending and updating workflows..." ) ;
await sendCommandListByDevChatRun ( ) ;
// Then asynchronously execute updateWorkflows
await dcClient . updateWorkflows ( ) ;
await dcClient . updateCustomWorkflows ( ) ;
await sendCommandListByDevChatRun ( ) ;
}
2024-03-07 18:50:38 +08:00
}
2024-12-02 14:40:19 +08:00
2024-11-28 13:12:11 +08:00
// Ensure the panel is activated
await ensureChatPanel ( context ) ;
2024-03-07 18:50:38 +08:00
}
) ;
2023-08-30 16:39:47 +08:00
2024-03-07 18:50:38 +08:00
context . subscriptions . push ( disposable ) ;
}
2023-12-20 12:57:55 +08:00
2023-08-30 16:39:47 +08:00
2024-07-16 07:55:02 +08:00
export function registerStartLocalServiceCommand (
context : vscode.ExtensionContext
) {
let disposable = vscode . commands . registerCommand (
"DevChat.StartLocalService" ,
async ( ) = > {
try {
const workspaceDir = UiUtilWrapper . workspaceFoldersFirstPath ( ) ? ? '' ;
logger . channel ( ) ? . debug ( ` extensionPath: ${ context . extensionPath } ` ) ;
logger . channel ( ) ? . debug ( ` workspacePath: ${ workspaceDir } ` ) ;
const port = await startLocalService ( context . extensionPath , workspaceDir ) ;
logger . channel ( ) ? . debug ( ` Local service started on port ${ port } ` ) ;
} catch ( error ) {
logger . channel ( ) ? . error ( 'Failed to start local service:' , error ) ;
}
}
) ;
context . subscriptions . push ( disposable ) ;
}
2024-03-07 18:50:38 +08:00
export function registerDevChatChatCommand ( context : vscode.ExtensionContext ) {
let disposable = vscode . commands . registerCommand (
"DevChat.Chat" ,
async ( message : string ) = > {
ensureChatPanel ( context ) ;
if ( ! ExtensionContextHolder . provider ? . view ( ) ) {
// wait 2 seconds
await new Promise ( ( resolve , reject ) = > {
setTimeout ( ( ) = > {
resolve ( true ) ;
} , 2000 ) ;
} ) ;
}
chatWithDevChat ( ExtensionContextHolder . provider ? . view ( ) ! , message ) ;
}
) ;
2023-09-13 10:08:16 +08:00
2024-03-07 18:50:38 +08:00
context . subscriptions . push ( disposable ) ;
2023-09-13 10:08:16 +08:00
}
2024-03-08 16:11:57 +08:00
export function registerCodeLensRangeCommand ( context : vscode.ExtensionContext ) {
2024-03-07 18:50:38 +08:00
let disposable = vscode . commands . registerCommand (
2024-03-08 16:11:57 +08:00
"CodeLens.Range" ,
2024-03-07 18:50:38 +08:00
async ( message : string , pos : { start : number ; end : number } ) = > {
const editor = vscode . window . activeTextEditor ;
if ( editor ) {
const range = new vscode . Range (
new vscode . Position ( pos . start , 0 ) ,
2024-03-12 21:20:03 +08:00
new vscode . Position ( pos . end + 1 , 0 )
2024-03-07 18:50:38 +08:00
) ;
editor . selection = new vscode . Selection ( range . start , range . end ) ;
}
ensureChatPanel ( context ) ;
if ( ! ExtensionContextHolder . provider ? . view ( ) ) {
// wait 2 seconds
await new Promise ( ( resolve , reject ) = > {
setTimeout ( ( ) = > {
resolve ( true ) ;
} , 2000 ) ;
} ) ;
}
chatWithDevChat ( ExtensionContextHolder . provider ? . view ( ) ! , message ) ;
}
) ;
context . subscriptions . push ( disposable ) ;
2023-11-23 13:02:02 +08:00
}
2023-08-21 11:52:00 +08:00
2024-03-07 18:50:38 +08:00
export function registerHandleUri ( context : vscode.ExtensionContext ) {
context . subscriptions . push (
vscode . window . registerUriHandler ( {
async handleUri ( uri ) {
// 解析 URI 并执行相应的操作
if ( uri . path . includes ( "accesskey" ) ) {
const accessKey = uri . path . split ( "/" ) [ 2 ] ;
2024-06-17 21:33:30 +08:00
DevChatConfig . getInstance ( ) . set ( "provides.devchat.api_key" , accessKey ) ;
DevChatConfig . getInstance ( ) . set ( "provides.devchat.api_base" , "https://api.devchat.ai/v1" ) ;
2024-03-07 18:50:38 +08:00
ensureChatPanel ( context ) ;
await new Promise ( ( resolve , reject ) = > {
setTimeout ( ( ) = > {
resolve ( true ) ;
} , 1000 ) ;
} ) ;
ExtensionContextHolder . provider ? . reloadWebview ( ) ;
}
} ,
} )
) ;
2023-12-12 16:39:08 +08:00
}
2024-04-01 17:15:44 +08:00
export function registerExplainCommand ( context : vscode.ExtensionContext ) {
2024-03-07 18:50:38 +08:00
const callback = async ( ) = > {
const editor = vscode . window . activeTextEditor ;
if ( editor ) {
if ( ! ( await ensureChatPanel ( context ) ) ) {
return ;
}
chatWithDevChat ( ExtensionContextHolder . provider ? . view ( ) ! , "/explain" ) ;
}
} ;
context . subscriptions . push (
vscode . commands . registerCommand ( "devchat.explain" , callback )
) ;
context . subscriptions . push (
vscode . commands . registerCommand ( "devchat.explain_chinese" , callback )
) ;
2024-01-18 17:52:55 +08:00
}
2024-04-01 17:15:44 +08:00
export function registerCommentCommand ( context : vscode.ExtensionContext ) {
2024-03-08 16:11:57 +08:00
const callback = async ( ) = > {
const editor = vscode . window . activeTextEditor ;
if ( editor ) {
if ( ! ( await ensureChatPanel ( context ) ) ) {
return ;
}
chatWithDevChat ( ExtensionContextHolder . provider ? . view ( ) ! , "/comments" ) ;
}
} ;
context . subscriptions . push (
vscode . commands . registerCommand ( "devchat.comments" , callback )
) ;
context . subscriptions . push (
vscode . commands . registerCommand ( "devchat.comments_chinese" , callback )
) ;
}
2024-04-01 17:15:44 +08:00
export function registerFixCommand ( context : vscode.ExtensionContext ) {
2024-03-12 21:20:03 +08:00
const callback = async ( ) = > {
const editor = vscode . window . activeTextEditor ;
if ( editor ) {
if ( ! ( await ensureChatPanel ( context ) ) ) {
return ;
}
chatWithDevChat ( ExtensionContextHolder . provider ? . view ( ) ! , "/fix" ) ;
}
} ;
context . subscriptions . push (
vscode . commands . registerCommand ( "devchat.fix" , callback )
) ;
context . subscriptions . push (
vscode . commands . registerCommand ( "devchat.fix_chinese" , callback )
) ;
}
2024-04-17 19:42:56 +08:00
2024-06-18 15:18:05 +08:00
export async function registerQuickFixCommand ( context : vscode.ExtensionContext ) {
2024-04-17 19:42:56 +08:00
let disposable = vscode . commands . registerCommand (
2024-07-18 15:28:10 +08:00
"DevChat.quickFixAskDevChat" ,
2024-06-26 10:13:57 +08:00
async ( document : vscode . TextDocument , range : vscode.Range | vscode . Selection , diagnostic : vscode.Diagnostic ) = > {
2024-04-17 19:42:56 +08:00
ensureChatPanel ( context ) ;
if ( ! ExtensionContextHolder . provider ? . view ( ) ) {
2024-04-18 11:57:46 +08:00
await waitForPanelActivation ( ) ;
2024-04-17 19:42:56 +08:00
}
2024-06-26 10:13:57 +08:00
// select the code
const editor = vscode . window . activeTextEditor ;
editor ! . selection = new vscode . Selection ( range . start , range . end ) ;
2024-07-18 15:28:10 +08:00
chatWithDevChat ( ExtensionContextHolder . provider ? . view ( ) ! , "/ask_issue " ) ;
2024-04-17 19:42:56 +08:00
}
) ;
2024-07-18 15:28:10 +08:00
let disposableFixUsingDevChat = vscode . commands . registerCommand (
"DevChat.quickFixUsingDevChat" ,
async ( document : vscode . TextDocument , range : vscode.Range | vscode . Selection , diagnostic : vscode.Diagnostic ) = > {
ensureChatPanel ( context ) ;
if ( ! ExtensionContextHolder . provider ? . view ( ) ) {
await waitForPanelActivation ( ) ;
}
// select the code
const editor = vscode . window . activeTextEditor ;
editor ! . selection = new vscode . Selection ( range . start , range . end ) ;
chatWithDevChat ( ExtensionContextHolder . provider ? . view ( ) ! , "/fix_issue " ) ;
}
) ;
2024-04-17 19:42:56 +08:00
context . subscriptions . push ( disposable ) ;
2024-07-18 15:28:10 +08:00
context . subscriptions . push ( disposableFixUsingDevChat ) ;
2024-04-18 11:57:46 +08:00
}
async function waitForPanelActivation() {
return new Promise ( ( resolve ) = > {
setTimeout ( ( ) = > {
resolve ( true ) ;
} , 2000 ) ;
} ) ;
}
2024-06-18 15:18:05 +08:00
async function generatePrompt ( code : string , surroundingCode : string , diagnosticMessage : string , language : string ) {
const editor = vscode . window . activeTextEditor ;
if ( editor ) {
const selectedText = editor . document . getText ( editor . selection ) ;
await sendCodeSelectMessage (
ExtensionContextHolder . provider ? . view ( ) ! ,
editor . document . fileName ,
code ,
0
) ;
// wait 1 second
await new Promise ( ( resolve ) = > setTimeout ( resolve , 1000 ) ) ;
return ` Context code is current edit file. \ n \ nThere is an error in the context code: \ n \` \` \` \ n ${ surroundingCode } \ n \` \` \` \ n \ nHow do I fix this problem in the above code?: ${ diagnosticMessage } , please output steps to fix it. ${ language === "zh" ? "结果输出请使用中文。" : "" } ` ;
}
return ` 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 } , please output steps to fix it. ${ language === "zh" ? "结果输出请使用中文。" : "" } ` ;
2024-04-17 19:42:56 +08:00
}