Merge pull request #427 from devchat-ai/reorganize-ide-service

Reorganize ide service module
This commit is contained in:
boob.yang 2024-02-01 11:32:58 +08:00 committed by GitHub
commit c8b05e1da7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 622 additions and 457 deletions

View File

@ -0,0 +1,3 @@
# IDE Service
This module is the VSCode implementation of IDE Service Protocol.

View 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;
}

View 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;
}

View 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;
}

View 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();
}

View 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;
}
}

View 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();
}
}

View File

@ -0,0 +1,7 @@
import * as vscode from 'vscode';
export async function updateSlashCommands() {
vscode.commands.executeCommand('DevChat.InstallCommands');
return true;
}

View File

@ -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);
}
});
};

View File

@ -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.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));
}
},
"/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}`;
});
}
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}`;
});
}