Merge pull request #427 from devchat-ai/reorganize-ide-service
Reorganize ide service module
This commit is contained in:
commit
c8b05e1da7
3
src/ide_services/README.md
Normal file
3
src/ide_services/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# IDE Service
|
||||
|
||||
This module is the VSCode implementation of IDE Service Protocol.
|
9
src/ide_services/endpoints/getServicePort.ts
Normal file
9
src/ide_services/endpoints/getServicePort.ts
Normal file
@ -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;
|
||||
}
|
7
src/ide_services/endpoints/ideLanguage.ts
Normal file
7
src/ide_services/endpoints/ideLanguage.ts
Normal file
@ -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;
|
||||
}
|
14
src/ide_services/endpoints/ideLogging.ts
Normal file
14
src/ide_services/endpoints/ideLogging.ts
Normal file
@ -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;
|
||||
}
|
68
src/ide_services/endpoints/installPythonEnv.ts
Normal file
68
src/ide_services/endpoints/installPythonEnv.ts
Normal file
@ -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();
|
||||
}
|
47
src/ide_services/endpoints/legacy.ts
Normal file
47
src/ide_services/endpoints/legacy.ts
Normal file
@ -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;
|
||||
}
|
||||
}
|
271
src/ide_services/endpoints/unofficial.ts
Normal file
271
src/ide_services/endpoints/unofficial.ts
Normal file
@ -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<any[]> => {
|
||||
// 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();
|
||||
}
|
||||
}
|
7
src/ide_services/endpoints/updateSlashCommands.ts
Normal file
7
src/ide_services/endpoints/updateSlashCommands.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
|
||||
export async function updateSlashCommands() {
|
||||
vscode.commands.executeCommand('DevChat.InstallCommands');
|
||||
return true;
|
||||
}
|
@ -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<any[]> => {
|
||||
// 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);
|
||||
}
|
||||
});
|
||||
};
|
@ -1,295 +1,124 @@
|
||||
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
|
||||
/**
|
||||
* Official IDE Service Protocol Endpoints
|
||||
*/
|
||||
"/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;
|
||||
}
|
||||
keys: [],
|
||||
handler: getServicePort,
|
||||
},
|
||||
"/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;
|
||||
|
||||
return {
|
||||
"filePath": filePath,
|
||||
"visibleText": visibleText,
|
||||
"visibleRange": [visibleRange.start.line, visibleRange.end.line]
|
||||
|
||||
};
|
||||
} 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;
|
||||
|
||||
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.DocumentSymbol[] | vscode.SymbolInformation[]>('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.SymbolInformation[]>('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));
|
||||
}
|
||||
"/install_python_env": {
|
||||
keys: ["command_name", "requirements_file"],
|
||||
handler: installPythonEnv,
|
||||
},
|
||||
"/update_slash_commands": {
|
||||
"keys": [],
|
||||
"handler": async () => {
|
||||
vscode.commands.executeCommand('DevChat.InstallCommands');
|
||||
return true;
|
||||
}
|
||||
keys: [],
|
||||
handler: updateSlashCommands,
|
||||
},
|
||||
// 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;
|
||||
}
|
||||
keys: [],
|
||||
handler: ideLanguage,
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
"/log_info": {
|
||||
"keys": ["message"],
|
||||
"handler": async (message: string) => {
|
||||
logger.channel()?.info(message);
|
||||
return true;
|
||||
}
|
||||
keys: ["message"],
|
||||
handler: logInfo,
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
"/log_warn": {
|
||||
"keys": ["message"],
|
||||
"handler": async (message: string) => {
|
||||
logger.channel()?.warn(message);
|
||||
return true;
|
||||
}
|
||||
keys: ["message"],
|
||||
handler: logWarn,
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
"/log_error": {
|
||||
"keys": ["message"],
|
||||
"handler": async (message: string) => {
|
||||
logger.channel()?.error(message);
|
||||
return true;
|
||||
}
|
||||
keys: ["message"],
|
||||
handler: logError,
|
||||
},
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
"/definitions": {
|
||||
keys: ["abspath", "line", "character", "token"],
|
||||
handler: LegacyEndpoints.definitions,
|
||||
},
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
"/references": {
|
||||
keys: ["abspath", "line", "character"],
|
||||
handler: LegacyEndpoints.references,
|
||||
},
|
||||
|
||||
/**
|
||||
* Unofficial endpoints
|
||||
*/
|
||||
"/visible_lines": {
|
||||
keys: [],
|
||||
handler: UnofficialEndpoints.visibleLines,
|
||||
},
|
||||
"/selected_lines": {
|
||||
keys: [],
|
||||
handler: UnofficialEndpoints.selectedLines,
|
||||
},
|
||||
"/diff_apply": {
|
||||
keys: ["filepath", "content"],
|
||||
handler: UnofficialEndpoints.diffApply,
|
||||
},
|
||||
|
||||
"/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,
|
||||
},
|
||||
// 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;
|
||||
}
|
||||
keys: ["folder"],
|
||||
handler: UnofficialEndpoints.openFolder,
|
||||
},
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
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') {
|
||||
logger.channel()?.info(`request: ${parsedUrl}`);
|
||||
if (parsedUrl.pathname === "/favicon.ico") {
|
||||
res.writeHead(204);
|
||||
res.end();
|
||||
return;
|
||||
@ -297,20 +126,23 @@ export async function startRpcServer() {
|
||||
|
||||
let params: any = {};
|
||||
|
||||
if (req.method === 'POST') {
|
||||
let body = '';
|
||||
req.on('data', chunk => {
|
||||
if (req.method === "POST") {
|
||||
let body = "";
|
||||
req.on("data", (chunk) => {
|
||||
body += chunk.toString(); // 将Buffer转换为string
|
||||
});
|
||||
|
||||
req.on('end', () => {
|
||||
req.on("end", () => {
|
||||
// 根据不同Content-Type,进行不同方式的解析
|
||||
if (req.headers['content-type'] === 'application/json') {
|
||||
if (req.headers["content-type"] === "application/json") {
|
||||
// 解析JSON格式的数据
|
||||
params = JSON.parse(body);
|
||||
|
||||
// 处理postParams
|
||||
} else if (req.headers['content-type'] === 'application/x-www-form-urlencoded') {
|
||||
} else if (
|
||||
req.headers["content-type"] ===
|
||||
"application/x-www-form-urlencoded"
|
||||
) {
|
||||
// 解析URL编码的数据
|
||||
params = querystring.parse(body);
|
||||
// 处理postParams
|
||||
@ -318,7 +150,7 @@ export async function startRpcServer() {
|
||||
|
||||
handleRequest(parsedUrl, params, res);
|
||||
});
|
||||
} else if (req.method === 'GET') {
|
||||
} else if (req.method === "GET") {
|
||||
const queryParams = parsedUrl.searchParams;
|
||||
for (let param of queryParams) {
|
||||
params[param[0]] = param[1];
|
||||
@ -328,14 +160,18 @@ export async function startRpcServer() {
|
||||
}
|
||||
});
|
||||
|
||||
async function handleRequest(parsedUrl: URL, params: any, res: http.ServerResponse) {
|
||||
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']) {
|
||||
for (let key of functionRegistry[parsedUrl.pathname]["keys"]) {
|
||||
if (!params.hasOwnProperty(key)) {
|
||||
// check whether key in functionRegistry[parsedUrl.pathname]['optional']
|
||||
newParameters.push(undefined);
|
||||
@ -344,19 +180,24 @@ export async function startRpcServer() {
|
||||
newParameters.push(params[key]);
|
||||
}
|
||||
if (!keysExist) {
|
||||
responseResult['error'] = "Missing required parameters";
|
||||
responseResult["error"] = "Missing required parameters";
|
||||
} else {
|
||||
responseResult['result'] = await functionRegistry[parsedUrl.pathname]['handler'](...newParameters);
|
||||
if (parsedUrl.pathname === "/definitions" || parsedUrl.pathname === "/references") {
|
||||
responseResult = responseResult['result'];
|
||||
responseResult["result"] = await functionRegistry[
|
||||
parsedUrl.pathname
|
||||
]["handler"](...newParameters);
|
||||
if (
|
||||
parsedUrl.pathname === "/definitions" ||
|
||||
parsedUrl.pathname === "/references"
|
||||
) {
|
||||
responseResult = responseResult["result"];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
responseResult['error'] = "Function not found";
|
||||
responseResult["error"] = "Function not found";
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify(responseResult));
|
||||
} catch (error) {
|
||||
logger.channel()?.error(`Error: ${error}`);
|
||||
@ -367,7 +208,7 @@ export async function startRpcServer() {
|
||||
server.listen(0, () => {
|
||||
const address = server!.address();
|
||||
// `address()`返回的对象包含`port`属性,它是系统分配的端口号
|
||||
const port = typeof address === 'string' ? 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}`;
|
||||
|
Loading…
x
Reference in New Issue
Block a user