diff --git a/src/ide_services/README.md b/src/ide_services/README.md new file mode 100644 index 0000000..bbfaa6d --- /dev/null +++ b/src/ide_services/README.md @@ -0,0 +1,3 @@ +# IDE Service + +This module is the VSCode implementation of IDE Service Protocol. \ No newline at end of file diff --git a/src/ide_services/endpoints/getServicePort.ts b/src/ide_services/endpoints/getServicePort.ts new file mode 100644 index 0000000..a2a42a4 --- /dev/null +++ b/src/ide_services/endpoints/getServicePort.ts @@ -0,0 +1,9 @@ +import { logger } from "../../util/logger"; + +export async function getServicePort() { + logger + .channel() + ?.info(`get lsp bridge port: ${process.env.DEVCHAT_IDE_SERVICE_PORT}`); + // return await UiUtilWrapper.getLSPBrigePort(); + return process.env.DEVCHAT_IDE_SERVICE_PORT; +} diff --git a/src/ide_services/endpoints/ideLanguage.ts b/src/ide_services/endpoints/ideLanguage.ts new file mode 100644 index 0000000..314c42d --- /dev/null +++ b/src/ide_services/endpoints/ideLanguage.ts @@ -0,0 +1,7 @@ +import { UiUtilWrapper } from "../../util/uiUtil"; + +export async function ideLanguage() { + const language = UiUtilWrapper.getConfiguration("DevChat", "Language"); + // 'en' stands for English, 'zh' stands for Simplified Chinese + return language; +} diff --git a/src/ide_services/endpoints/ideLogging.ts b/src/ide_services/endpoints/ideLogging.ts new file mode 100644 index 0000000..a7b022f --- /dev/null +++ b/src/ide_services/endpoints/ideLogging.ts @@ -0,0 +1,14 @@ +import { logger } from "../../util/logger"; + +export async function logInfo(message: string) { + logger.channel()?.info(message); + return true; +} +export async function logWarn(message: string) { + logger.channel()?.warn(message); + return true; +} +export async function logError(message: string) { + logger.channel()?.error(message); + return true; +} diff --git a/src/ide_services/endpoints/installPythonEnv.ts b/src/ide_services/endpoints/installPythonEnv.ts new file mode 100644 index 0000000..a9279ae --- /dev/null +++ b/src/ide_services/endpoints/installPythonEnv.ts @@ -0,0 +1,68 @@ +import { logger } from "../../util/logger"; + +import { + createEnvByConda, + createEnvByMamba, +} from "../../util/python_installer/app_install"; +import { installRequirements } from "../../util/python_installer/package_install"; + +export async function installPythonEnv( + command_name: string, + requirements_file: string +) { + // 1. install python >= 3.11 + logger.channel()?.info(`create env for python ...`); + logger.channel()?.info(`try to create env by mamba ...`); + let pythonCommand = await createEnvByMamba(command_name, "", "3.11.4"); + + if (!pythonCommand || pythonCommand === "") { + logger + .channel() + ?.info( + `create env by mamba failed, try to create env by conda ...` + ); + pythonCommand = await createEnvByConda(command_name, "", "3.11.4"); + } + + if (!pythonCommand || pythonCommand === "") { + logger + .channel() + ?.error( + `create virtual python env failed, you need create it by yourself with command: "conda create -n devchat-commands python=3.11.4"` + ); + logger.channel()?.show(); + + return ""; + } + + // 3. install requirements.txt + // run command: pip install -r {requirementsFile} + let isInstalled = false; + // try 3 times + for (let i = 0; i < 4; i++) { + let otherSource: string | undefined = undefined; + if (i > 1) { + otherSource = "https://pypi.tuna.tsinghua.edu.cn/simple/"; + } + isInstalled = await installRequirements( + pythonCommand, + requirements_file, + otherSource + ); + if (isInstalled) { + break; + } + logger.channel()?.info(`Install packages failed, try again: ${i + 1}`); + } + if (!isInstalled) { + logger + .channel() + ?.error( + `Install packages failed, you can install it with command: "${pythonCommand} -m pip install -r ${requirements_file}"` + ); + logger.channel()?.show(); + return ""; + } + + return pythonCommand.trim(); +} diff --git a/src/ide_services/endpoints/legacy.ts b/src/ide_services/endpoints/legacy.ts new file mode 100644 index 0000000..0fe75a3 --- /dev/null +++ b/src/ide_services/endpoints/legacy.ts @@ -0,0 +1,47 @@ +/** + * Legacy endpoints migrated from language-bridge for ask-code + * + * Should remove these endpoints after ask-code migrated to new endpoints + */ + +import { + findDefinitions, + findDefinitionsOfToken, +} from "./legacy_bridge/feature/find-defs"; + +import { findReferences } from "./legacy_bridge/feature/find-refs"; + +export namespace LegacyEndpoints { + export async function definitions( + abspath: string, + line: string | undefined = undefined, + character: string | undefined = undefined, + token: string | undefined = undefined + ) { + if (token !== undefined) { + const definitions = await findDefinitionsOfToken(abspath, token); + return definitions; + } else { + const definitions = await findDefinitions( + abspath, + Number(line), + Number(character) + ); + return definitions; + } + } + + export async function references( + abspath: string, + line: number, + character: number + ) { + const references = await findReferences( + abspath, + Number(line), + Number(character) + ); + + return references; + } +} diff --git a/src/ide_services/lsp_bridge/feature/find-defs.ts b/src/ide_services/endpoints/legacy_bridge/feature/find-defs.ts similarity index 100% rename from src/ide_services/lsp_bridge/feature/find-defs.ts rename to src/ide_services/endpoints/legacy_bridge/feature/find-defs.ts diff --git a/src/ide_services/lsp_bridge/feature/find-refs.ts b/src/ide_services/endpoints/legacy_bridge/feature/find-refs.ts similarity index 100% rename from src/ide_services/lsp_bridge/feature/find-refs.ts rename to src/ide_services/endpoints/legacy_bridge/feature/find-refs.ts diff --git a/src/ide_services/endpoints/unofficial.ts b/src/ide_services/endpoints/unofficial.ts new file mode 100644 index 0000000..4e11130 --- /dev/null +++ b/src/ide_services/endpoints/unofficial.ts @@ -0,0 +1,271 @@ +import * as vscode from "vscode"; +import { applyCodeWithDiff } from "../../handler/diffHandler"; +import { getSymbolDefines } from "../../context/contextRefDefs"; + +// Function to convert vscode.Range to a plain object with line and column information +const convertRange = (range: vscode.Range | undefined): any => { + if (!range) { + return { + start_line: -1, + start_col: -1, + end_line: -1, + end_col: -1, + }; + } + return { + start_line: range.start.line, + start_col: range.start.character, + end_line: range.end.line, + end_col: range.end.character, + }; +}; + +// Generic function to execute a provider command and convert the results to a JSON serializable format +const executeProviderCommand = async ( + command: string, + abspath: string, + line: number, + col: number +): Promise => { + // Create a Position object from the line and col parameters + const position = new vscode.Position(line, col); + + // A promise that resolves to an array of Location or LocationLink instances. + const providerResults = await vscode.commands.executeCommand< + (vscode.Location | vscode.LocationLink)[] + >(command, vscode.Uri.file(abspath), position); + + if (!providerResults) { + return []; + } + + // Convert providerResults to a plain object array for JSON serialization + const resultsAsJson = providerResults.map((result) => { + if ("uri" in result) { + // Handle vscode.Location + return { + uri: result.uri.toString(), // Convert URI to string for JSON serialization + range: convertRange(result.range), + }; + } else { + // Handle vscode.LocationLink + return { + originSelectionRange: result.originSelectionRange + ? convertRange(result.originSelectionRange) + : undefined, + targetUri: result.targetUri.toString(), // Convert URI to string for JSON serialization + targetRange: convertRange(result.targetRange), + targetSelectionRange: convertRange(result.targetSelectionRange), + }; + } + }); + + return resultsAsJson; +}; + +// Function to recursively convert DocumentSymbol to a plain object +const convertDocumentSymbol = (symbol: vscode.DocumentSymbol): any => { + return { + name: symbol.name, + kind: symbol.kind, + detail: symbol.detail, + range: convertRange(symbol.range), + selectionRange: convertRange(symbol.selectionRange), + children: symbol.children.map(convertDocumentSymbol), // Recursively convert children + }; +}; + +// Function to convert SymbolInformation to a plain object +const convertSymbolInformation = (symbol: vscode.SymbolInformation): any => { + return { + name: symbol.name, + kind: symbol.kind, + location: { + uri: symbol.location.uri.toString(), // Convert URI to string for JSON serialization + range: convertRange(symbol.location.range), + }, + containerName: symbol.containerName, + }; +}; + +// Generic function to convert an array of DocumentSymbol or SymbolInformation to a plain object array +const convertSymbolsToPlainObjects = ( + symbols: vscode.DocumentSymbol[] | vscode.SymbolInformation[] +): any[] => { + return symbols.map((symbol) => { + if (symbol.children) { + // Handle DocumentSymbol with recursive conversion + return convertDocumentSymbol(symbol); + } else { + // Handle SymbolInformation + return convertSymbolInformation(symbol); + } + }); +}; + +export namespace UnofficialEndpoints { + export async function visibleLines() { + const editor = vscode.window.activeTextEditor; + if (editor) { + const visibleRanges = editor.visibleRanges; + const visibleRange = visibleRanges[0]; + const visibleText = editor.document.getText(visibleRange); + const filePath = editor.document.uri.fsPath; + + return { + filePath: filePath, + visibleText: visibleText, + visibleRange: [visibleRange.start.line, visibleRange.end.line], + }; + } else { + return { + filePath: "", + visibleText: "", + visibleRange: [-1, -1], + }; + } + } + + export async function selectedLines() { + const editor = vscode.window.activeTextEditor; + if (editor) { + const selection = editor.selection; + const selectedText = editor.document.getText(selection); + const startLine = selection.start.line; // VS Code API uses 0-based indexing for lines + const endLine = selection.end.line; + const charCount = selectedText.length; + const filePath = editor.document.uri.fsPath; + + return { + filePath: filePath, + selectedText: selectedText, + selectedRange: [ + startLine, + selection.start.character, + endLine, + selection.end.character, + ], + }; + } else { + return { + filePath: "", + selectedText: "", + selectedRange: [-1, -1, -1, -1], + }; + } + } + + export async function diffApply(filepath: string, content: string) { + applyCodeWithDiff({ fileName: filepath, content: content }, undefined); + return true; + } + + export async function documentSymbols(abspath: string) { + // A promise that resolves to an array of SymbolInformation and DocumentSymbol instances. + const documentSymbols = await vscode.commands.executeCommand< + vscode.DocumentSymbol[] | vscode.SymbolInformation[] + >("vscode.executeDocumentSymbolProvider", vscode.Uri.file(abspath)); + if (!documentSymbols) { + return []; + } + + const symbols = convertSymbolsToPlainObjects(documentSymbols); + return symbols; + } + + export async function workspaceSymbols(query: string) { + // A promise that resolves to an array of SymbolInformation and DocumentSymbol instances. + const querySymbols = await vscode.commands.executeCommand< + vscode.SymbolInformation[] + >("vscode.executeWorkspaceSymbolProvider", query); + if (!querySymbols) { + return []; + } + + return convertSymbolsToPlainObjects(querySymbols); + } + + export async function findDefinition( + abspath: string, + line: string, + col: string + ) { + return await executeProviderCommand( + "vscode.executeDefinitionProvider", + abspath, + Number(line), + Number(col) + ); + } + + export async function findTypeDefinition( + abspath: string, + line: string, + col: string + ) { + return await executeProviderCommand( + "vscode.executeTypeDefinitionProvider", + abspath, + Number(line), + Number(col) + ); + } + + export async function findDeclaration( + abspath: string, + line: string, + col: string + ) { + return await executeProviderCommand( + "vscode.executeDeclarationProvider", + abspath, + Number(line), + Number(col) + ); + } + + export async function findImplementation( + abspath: string, + line: string, + col: string + ) { + return await executeProviderCommand( + "vscode.executeImplementationProvider", + abspath, + Number(line), + Number(col) + ); + } + + export async function findReference( + abspath: string, + line: string, + col: string + ) { + return await executeProviderCommand( + "vscode.executeReferenceProvider", + abspath, + Number(line), + Number(col) + ); + } + + export async function openFolder(folder: string) { + // open folder by vscode + const folderPathParsed = folder.replace("\\", "/"); + // Updated Uri.parse to Uri.file + const folderUri = vscode.Uri.file(folderPathParsed); + vscode.commands.executeCommand(`vscode.openFolder`, folderUri); + return true; + } + + export async function getSymbolDefinesInSelectedCode() { + // find needed symbol defines in current editor document + // return value is a list of symbol defines + // each define has three fileds: + // path: file path contain that symbol define + // startLine: start line in that file + // content: source code for symbol define + return getSymbolDefines(); + } +} diff --git a/src/ide_services/endpoints/updateSlashCommands.ts b/src/ide_services/endpoints/updateSlashCommands.ts new file mode 100644 index 0000000..3274635 --- /dev/null +++ b/src/ide_services/endpoints/updateSlashCommands.ts @@ -0,0 +1,7 @@ +import * as vscode from 'vscode'; + + +export async function updateSlashCommands() { + vscode.commands.executeCommand('DevChat.InstallCommands'); + return true; +} \ No newline at end of file diff --git a/src/ide_services/lsp/lsp.ts b/src/ide_services/lsp/lsp.ts deleted file mode 100644 index 9ede985..0000000 --- a/src/ide_services/lsp/lsp.ts +++ /dev/null @@ -1,102 +0,0 @@ - - -import * as vscode from 'vscode'; - -// Function to convert vscode.Range to a plain object with line and column information -export const convertRange = (range: vscode.Range | undefined): any => { - if (!range) { - return { - start_line: -1, - start_col: -1, - end_line: -1, - end_col: -1, - }; - } - return { - start_line: range.start.line, - start_col: range.start.character, - end_line: range.end.line, - end_col: range.end.character - }; -}; - -// Generic function to execute a provider command and convert the results to a JSON serializable format -export const executeProviderCommand = async ( - command: string, - abspath: string, - line: number, - col: number -): Promise => { - // Create a Position object from the line and col parameters - const position = new vscode.Position(line, col); - - // A promise that resolves to an array of Location or LocationLink instances. - const providerResults = await vscode.commands.executeCommand<(vscode.Location | vscode.LocationLink)[]>( - command, - vscode.Uri.file(abspath), - position - ); - - if (!providerResults) { - return []; - } - - // Convert providerResults to a plain object array for JSON serialization - const resultsAsJson = providerResults.map(result => { - if ('uri' in result) { - // Handle vscode.Location - return { - uri: result.uri.toString(), // Convert URI to string for JSON serialization - range: convertRange(result.range) - }; - } else { - // Handle vscode.LocationLink - return { - originSelectionRange: result.originSelectionRange ? convertRange(result.originSelectionRange) : undefined, - targetUri: result.targetUri.toString(), // Convert URI to string for JSON serialization - targetRange: convertRange(result.targetRange), - targetSelectionRange: convertRange(result.targetSelectionRange) - }; - } - }); - - return resultsAsJson; -}; - -// Function to recursively convert DocumentSymbol to a plain object -const convertDocumentSymbol = (symbol: vscode.DocumentSymbol): any => { - return { - name: symbol.name, - kind: symbol.kind, - detail: symbol.detail, - range: convertRange(symbol.range), - selectionRange: convertRange(symbol.selectionRange), - children: symbol.children.map(convertDocumentSymbol) // Recursively convert children - }; -}; - -// Function to convert SymbolInformation to a plain object -const convertSymbolInformation = (symbol: vscode.SymbolInformation): any => { - return { - name: symbol.name, - kind: symbol.kind, - location: { - uri: symbol.location.uri.toString(), // Convert URI to string for JSON serialization - range: convertRange(symbol.location.range) - }, - containerName: symbol.containerName - }; -}; - -// Generic function to convert an array of DocumentSymbol or SymbolInformation to a plain object array -export const convertSymbolsToPlainObjects = (symbols: vscode.DocumentSymbol[] | vscode.SymbolInformation[]): any[] => { - return symbols.map(symbol => { - if (symbol.children) { - // Handle DocumentSymbol with recursive conversion - return convertDocumentSymbol(symbol); - } else { - // Handle SymbolInformation - return convertSymbolInformation(symbol); - } - }); -}; diff --git a/src/ide_services/services.ts b/src/ide_services/services.ts index 7a2e57c..c7ca760 100644 --- a/src/ide_services/services.ts +++ b/src/ide_services/services.ts @@ -1,375 +1,216 @@ -import * as http from 'http'; -import * as vscode from 'vscode'; -import * as path from 'path'; -import { exec } from 'child_process'; - -import * as querystring from 'querystring'; -import { logger } from '../util/logger'; -import { UiUtilWrapper } from '../util/uiUtil'; - -import { createEnvByConda, createEnvByMamba } from '../util/python_installer/app_install'; -import { installRequirements } from '../util/python_installer/package_install'; - -import { - findDefinitions, - findDefinitionsOfToken, -} from "./lsp_bridge/feature/find-defs"; - -import { findReferences } from "./lsp_bridge/feature/find-refs"; -import { convertSymbolsToPlainObjects, executeProviderCommand } from './lsp/lsp'; -import { applyCodeWithDiff } from '../handler/diffHandler'; -import { getSymbolDefines } from '../context/contextRefDefs'; +/* eslint-disable @typescript-eslint/naming-convention */ +import * as http from "http"; +import * as querystring from "querystring"; +import { logger } from "../util/logger"; +import { getServicePort } from "./endpoints/getServicePort"; +import { installPythonEnv } from "./endpoints/installPythonEnv"; +import { logError, logInfo, logWarn } from "./endpoints/ideLogging"; +import { updateSlashCommands } from "./endpoints/updateSlashCommands"; +import { ideLanguage } from "./endpoints/ideLanguage"; +import { LegacyEndpoints } from "./endpoints/legacy"; +import { UnofficialEndpoints } from "./endpoints/unofficial"; const functionRegistry: any = { - // eslint-disable-next-line @typescript-eslint/naming-convention - "/get_lsp_brige_port": { - "keys": [], - "handler": async () => { - logger.channel()?.info(`get lsp bridge port: ${process.env.DEVCHAT_IDE_SERVICE_PORT}`); - // return await UiUtilWrapper.getLSPBrigePort(); - return process.env.DEVCHAT_IDE_SERVICE_PORT; - } - }, - "/visible_lines": { - "keys": [], - "handler": async () => { - const editor = vscode.window.activeTextEditor; - if (editor) { - const visibleRanges = editor.visibleRanges; - const visibleRange = visibleRanges[0]; - const visibleText = editor.document.getText(visibleRange); - const filePath = editor.document.uri.fsPath; + /** + * Official IDE Service Protocol Endpoints + */ + "/get_lsp_brige_port": { + keys: [], + handler: getServicePort, + }, + "/install_python_env": { + keys: ["command_name", "requirements_file"], + handler: installPythonEnv, + }, + "/update_slash_commands": { + keys: [], + handler: updateSlashCommands, + }, + "/ide_language": { + keys: [], + handler: ideLanguage, + }, + "/log_info": { + keys: ["message"], + handler: logInfo, + }, + "/log_warn": { + keys: ["message"], + handler: logWarn, + }, + "/log_error": { + keys: ["message"], + handler: logError, + }, - return { - "filePath": filePath, - "visibleText": visibleText, - "visibleRange": [visibleRange.start.line, visibleRange.end.line] + /** + * @deprecated + */ + "/definitions": { + keys: ["abspath", "line", "character", "token"], + handler: LegacyEndpoints.definitions, + }, + /** + * @deprecated + */ + "/references": { + keys: ["abspath", "line", "character"], + handler: LegacyEndpoints.references, + }, - }; - } else { - return { - "filePath": "", - "visibleText": "", - "visibleRange": [-1, -1] - }; - } - } - }, - "/selected_lines": { - "keys": [], - "handler": async () => { - const editor = vscode.window.activeTextEditor; - if (editor) { - const selection = editor.selection; - const selectedText = editor.document.getText(selection); - const startLine = selection.start.line; // VS Code API uses 0-based indexing for lines - const endLine = selection.end.line; - const charCount = selectedText.length; - const filePath = editor.document.uri.fsPath; + /** + * Unofficial endpoints + */ + "/visible_lines": { + keys: [], + handler: UnofficialEndpoints.visibleLines, + }, + "/selected_lines": { + keys: [], + handler: UnofficialEndpoints.selectedLines, + }, + "/diff_apply": { + keys: ["filepath", "content"], + handler: UnofficialEndpoints.diffApply, + }, - return { - "filePath": filePath, - "selectedText": selectedText, - "selectedRange": [startLine, selection.start.character, endLine, selection.end.character] - - }; - } else { - return { - "filePath": "", - "selectedText": "", - "selectedRange": [-1, -1, -1, -1] - }; - } - } - }, - "/diff_apply": { - "keys": ["filepath", "content"], - "handler": async (filepath: string, content: string) => { - applyCodeWithDiff({'fileName': filepath, 'content': content}, undefined ); - return true; - } - }, - "/definitions": { - "keys": ["abspath", "line", "character", "token"], - "handler": async (abspath: string, line: string | undefined = undefined, character: string | undefined = undefined, token: string | undefined = undefined) => { - if (token !== undefined) { - const definitions = await findDefinitionsOfToken(abspath, token); - return definitions; - } else { - const definitions = await findDefinitions(abspath, Number(line), Number(character)); - return definitions; - } - } - }, - "/references": { - "keys": ["abspath", "line", "character"], - "handler": async (abspath: string, line: number, character: number) => { - const references = await findReferences( - abspath, - Number(line), - Number(character) - ); - - return references; - } - }, - "/document_symbols": { - "keys": ["abspath"], - "handler": async (abspath: string) => { - // A promise that resolves to an array of SymbolInformation and DocumentSymbol instances. - const documentSymbols = await vscode.commands.executeCommand('vscode.executeDocumentSymbolProvider', vscode.Uri.file(abspath)); - if (!documentSymbols) { - return []; - } - - const symbols = convertSymbolsToPlainObjects(documentSymbols); - return symbols; - } - }, - "/workspace_symbols": { - "keys": ["query"], - "handler": async (query: string) => { - // A promise that resolves to an array of SymbolInformation and DocumentSymbol instances. - const querySymbols = await vscode.commands.executeCommand('vscode.executeWorkspaceSymbolProvider', query); - if (!querySymbols) { - return []; - } - - return convertSymbolsToPlainObjects(querySymbols); - } - }, - "/find_definition": { - "keys": ["abspath", "line", "col"], - "handler": async (abspath: string, line: string, col: string) => { - return await executeProviderCommand('vscode.executeDefinitionProvider', abspath, Number(line), Number(col)); - } - }, - "/find_type_definition": { - "keys": ["abspath", "line", "col"], - "handler": async (abspath: string, line: string, col: string) => { - return await executeProviderCommand('vscode.executeTypeDefinitionProvider', abspath, Number(line), Number(col)); - } - }, - "/find_declaration": { - "keys": ["abspath", "line", "col"], - "handler": async (abspath: string, line: string, col: string) => { - return await executeProviderCommand('vscode.executeDeclarationProvider', abspath, Number(line), Number(col)); - } - }, - "/find_implementation": { - "keys": ["abspath", "line", "col"], - "handler": async (abspath: string, line: string, col: string) => { - return await executeProviderCommand('vscode.executeImplementationProvider', abspath, Number(line), Number(col)); - } - }, - "/find_reference": { - "keys": ["abspath", "line", "col"], - "handler": async (abspath: string, line: string, col: string) => { - return await executeProviderCommand('vscode.executeReferenceProvider', abspath, Number(line), Number(col)); - } - }, - "/update_slash_commands": { - "keys": [], - "handler": async () => { - vscode.commands.executeCommand('DevChat.InstallCommands'); - return true; - } - }, - // eslint-disable-next-line @typescript-eslint/naming-convention - "/ide_language": { - "keys": [], - "handler": async () => { - const language = UiUtilWrapper.getConfiguration("DevChat", "Language"); - // 'en' stands for English, 'zh' stands for Simplified Chinese - return language; - } - }, - // eslint-disable-next-line @typescript-eslint/naming-convention - "/log_info": { - "keys": ["message"], - "handler": async (message: string) => { - logger.channel()?.info(message); - return true; - } - }, - // eslint-disable-next-line @typescript-eslint/naming-convention - "/log_warn": { - "keys": ["message"], - "handler": async (message: string) => { - logger.channel()?.warn(message); - return true; - } - }, - // eslint-disable-next-line @typescript-eslint/naming-convention - "/log_error": { - "keys": ["message"], - "handler": async (message: string) => { - logger.channel()?.error(message); - return true; - } - }, - // eslint-disable-next-line @typescript-eslint/naming-convention - "/open_folder": { - "keys": ["folder"], - "handler": async (folder: string) => { - // open folder by vscode - const folderPathParsed = folder.replace('\\', '/'); - // Updated Uri.parse to Uri.file - const folderUri = vscode.Uri.file(folderPathParsed); - vscode.commands.executeCommand(`vscode.openFolder`, folderUri); - return true; - } - }, - // eslint-disable-next-line @typescript-eslint/naming-convention - "/install_python_env": { - "keys": ["command_name", "requirements_file"], - // eslint-disable-next-line @typescript-eslint/naming-convention - "handler": async (command_name: string, requirements_file: string) => { - // 1. install python >= 3.11 - logger.channel()?.info(`create env for python ...`); - logger.channel()?.info(`try to create env by mamba ...`); - let pythonCommand = await createEnvByMamba(command_name, "", "3.11.4"); - - if (!pythonCommand || pythonCommand === "") { - logger.channel()?.info(`create env by mamba failed, try to create env by conda ...`); - pythonCommand = await createEnvByConda(command_name, "", "3.11.4"); - } - - if (!pythonCommand || pythonCommand === "") { - logger.channel()?.error(`create virtual python env failed, you need create it by yourself with command: "conda create -n devchat-commands python=3.11.4"`); - logger.channel()?.show(); - - return ""; - } - - // 3. install requirements.txt - // run command: pip install -r {requirementsFile} - let isInstalled = false; - // try 3 times - for (let i = 0; i < 4; i++) { - let otherSource: string | undefined = undefined; - if (i > 1) { - otherSource = 'https://pypi.tuna.tsinghua.edu.cn/simple/'; - } - isInstalled = await installRequirements(pythonCommand, requirements_file, otherSource); - if (isInstalled) { - break; - } - logger.channel()?.info(`Install packages failed, try again: ${i + 1}`); - } - if (!isInstalled) { - logger.channel()?.error(`Install packages failed, you can install it with command: "${pythonCommand} -m pip install -r ${requirements_file}"`); - logger.channel()?.show(); - return ''; - } - - return pythonCommand.trim(); - } - }, - // eslint-disable-next-line @typescript-eslint/naming-convention - "/get_symbol_defines_in_selected_code": { - "keys": [], - "handler": async () => { - // find needed symbol defines in current editor document - // return value is a list of symbol defines - // each define has three fileds: - // path: file path contain that symbol define - // startLine: start line in that file - // content: source code for symbol define - return getSymbolDefines(); - } - } + "/document_symbols": { + keys: ["abspath"], + handler: UnofficialEndpoints.documentSymbols, + }, + "/workspace_symbols": { + keys: ["query"], + handler: UnofficialEndpoints.workspaceSymbols, + }, + "/find_definition": { + keys: ["abspath", "line", "col"], + handler: UnofficialEndpoints.findDefinition, + }, + "/find_type_definition": { + keys: ["abspath", "line", "col"], + handler: UnofficialEndpoints.findTypeDefinition, + }, + "/find_declaration": { + keys: ["abspath", "line", "col"], + handler: UnofficialEndpoints.findTypeDefinition, + }, + "/find_implementation": { + keys: ["abspath", "line", "col"], + handler: UnofficialEndpoints.findImplementation, + }, + "/find_reference": { + keys: ["abspath", "line", "col"], + handler: UnofficialEndpoints.findReference, + }, + "/open_folder": { + keys: ["folder"], + handler: UnofficialEndpoints.openFolder, + }, + "/get_symbol_defines_in_selected_code": { + keys: [], + handler: UnofficialEndpoints.getSymbolDefinesInSelectedCode, + }, }; - - let server: http.Server | null = null; export async function startRpcServer() { - server = http.createServer((req, res) => { - const parsedUrl = new URL(req.url!, `http://${req.headers.host}`); - logger.channel()?.info(`request: ${parsedUrl}`) - if (parsedUrl.pathname === '/favicon.ico') { - res.writeHead(204); - res.end(); - return; - } + server = http.createServer((req, res) => { + const parsedUrl = new URL(req.url!, `http://${req.headers.host}`); + logger.channel()?.info(`request: ${parsedUrl}`); + if (parsedUrl.pathname === "/favicon.ico") { + res.writeHead(204); + res.end(); + return; + } - let params: any = {}; + let params: any = {}; - if (req.method === 'POST') { - let body = ''; - req.on('data', chunk => { - body += chunk.toString(); // 将Buffer转换为string - }); + if (req.method === "POST") { + let body = ""; + req.on("data", (chunk) => { + body += chunk.toString(); // 将Buffer转换为string + }); - req.on('end', () => { - // 根据不同Content-Type,进行不同方式的解析 - if (req.headers['content-type'] === 'application/json') { - // 解析JSON格式的数据 - params = JSON.parse(body); + req.on("end", () => { + // 根据不同Content-Type,进行不同方式的解析 + if (req.headers["content-type"] === "application/json") { + // 解析JSON格式的数据 + params = JSON.parse(body); - // 处理postParams - } else if (req.headers['content-type'] === 'application/x-www-form-urlencoded') { - // 解析URL编码的数据 - params = querystring.parse(body); - // 处理postParams - } + // 处理postParams + } else if ( + req.headers["content-type"] === + "application/x-www-form-urlencoded" + ) { + // 解析URL编码的数据 + params = querystring.parse(body); + // 处理postParams + } - handleRequest(parsedUrl, params, res); - }); - } else if (req.method === 'GET') { - const queryParams = parsedUrl.searchParams; - for (let param of queryParams) { - params[param[0]] = param[1]; - } + handleRequest(parsedUrl, params, res); + }); + } else if (req.method === "GET") { + const queryParams = parsedUrl.searchParams; + for (let param of queryParams) { + params[param[0]] = param[1]; + } - handleRequest(parsedUrl, params, res); - } - }); + handleRequest(parsedUrl, params, res); + } + }); - async function handleRequest(parsedUrl: URL, params: any, res: http.ServerResponse) { - try { - let responseResult = {}; + async function handleRequest( + parsedUrl: URL, + params: any, + res: http.ServerResponse + ) { + try { + let responseResult = {}; - if (functionRegistry[parsedUrl.pathname]) { - let keysExist = true; - let newParameters: any[] = []; - for (let key of functionRegistry[parsedUrl.pathname]['keys']) { - if (!params.hasOwnProperty(key)) { - // check whether key in functionRegistry[parsedUrl.pathname]['optional'] - newParameters.push(undefined); - continue; - } - newParameters.push(params[key]); - } - if (!keysExist) { - responseResult['error'] = "Missing required parameters"; - } else { - responseResult['result'] = await functionRegistry[parsedUrl.pathname]['handler'](...newParameters); - if (parsedUrl.pathname === "/definitions" || parsedUrl.pathname === "/references") { - responseResult = responseResult['result']; - } - } - } else { - responseResult['error'] = "Function not found"; - } + if (functionRegistry[parsedUrl.pathname]) { + let keysExist = true; + let newParameters: any[] = []; + for (let key of functionRegistry[parsedUrl.pathname]["keys"]) { + if (!params.hasOwnProperty(key)) { + // check whether key in functionRegistry[parsedUrl.pathname]['optional'] + newParameters.push(undefined); + continue; + } + newParameters.push(params[key]); + } + if (!keysExist) { + responseResult["error"] = "Missing required parameters"; + } else { + responseResult["result"] = await functionRegistry[ + parsedUrl.pathname + ]["handler"](...newParameters); + if ( + parsedUrl.pathname === "/definitions" || + parsedUrl.pathname === "/references" + ) { + responseResult = responseResult["result"]; + } + } + } else { + responseResult["error"] = "Function not found"; + } - // eslint-disable-next-line @typescript-eslint/naming-convention - res.writeHead(200, { 'Content-Type': 'application/json' }); - res.end(JSON.stringify(responseResult)); - } catch (error) { - logger.channel()?.error(`Error: ${error}`); - logger.channel()?.show(); - } - } + // eslint-disable-next-line @typescript-eslint/naming-convention + res.writeHead(200, { "Content-Type": "application/json" }); + res.end(JSON.stringify(responseResult)); + } catch (error) { + logger.channel()?.error(`Error: ${error}`); + logger.channel()?.show(); + } + } - server.listen(0, () => { - const address = server!.address(); - // `address()`返回的对象包含`port`属性,它是系统分配的端口号 - const port = typeof address === 'string' ? address : address?.port; - logger.channel()?.info(`Server running at http://localhost:${port}/`); - process.env.DEVCHAT_IDE_SERVICE_URL = `http://localhost:${port}`; - process.env.DEVCHAT_IDE_SERVICE_PORT = `${port}`; - }); -} \ No newline at end of file + server.listen(0, () => { + const address = server!.address(); + // `address()`返回的对象包含`port`属性,它是系统分配的端口号 + const port = typeof address === "string" ? address : address?.port; + logger.channel()?.info(`Server running at http://localhost:${port}/`); + process.env.DEVCHAT_IDE_SERVICE_URL = `http://localhost:${port}`; + process.env.DEVCHAT_IDE_SERVICE_PORT = `${port}`; + }); +}