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';
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
import * as vscode from 'vscode';
|
import * as http from "http";
|
||||||
import * as path from 'path';
|
import * as querystring from "querystring";
|
||||||
import { exec } from 'child_process';
|
import { logger } from "../util/logger";
|
||||||
|
|
||||||
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';
|
|
||||||
|
|
||||||
|
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 = {
|
const functionRegistry: any = {
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
/**
|
||||||
|
* Official IDE Service Protocol Endpoints
|
||||||
|
*/
|
||||||
"/get_lsp_brige_port": {
|
"/get_lsp_brige_port": {
|
||||||
"keys": [],
|
keys: [],
|
||||||
"handler": async () => {
|
handler: 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;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"/visible_lines": {
|
"/install_python_env": {
|
||||||
"keys": [],
|
keys: ["command_name", "requirements_file"],
|
||||||
"handler": async () => {
|
handler: installPythonEnv,
|
||||||
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));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"/update_slash_commands": {
|
"/update_slash_commands": {
|
||||||
"keys": [],
|
keys: [],
|
||||||
"handler": async () => {
|
handler: updateSlashCommands,
|
||||||
vscode.commands.executeCommand('DevChat.InstallCommands');
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
"/ide_language": {
|
"/ide_language": {
|
||||||
"keys": [],
|
keys: [],
|
||||||
"handler": async () => {
|
handler: ideLanguage,
|
||||||
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": {
|
"/log_info": {
|
||||||
"keys": ["message"],
|
keys: ["message"],
|
||||||
"handler": async (message: string) => {
|
handler: logInfo,
|
||||||
logger.channel()?.info(message);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
"/log_warn": {
|
"/log_warn": {
|
||||||
"keys": ["message"],
|
keys: ["message"],
|
||||||
"handler": async (message: string) => {
|
handler: logWarn,
|
||||||
logger.channel()?.warn(message);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
"/log_error": {
|
"/log_error": {
|
||||||
"keys": ["message"],
|
keys: ["message"],
|
||||||
"handler": async (message: string) => {
|
handler: logError,
|
||||||
logger.channel()?.error(message);
|
},
|
||||||
return true;
|
|
||||||
}
|
/**
|
||||||
|
* @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": {
|
"/open_folder": {
|
||||||
"keys": ["folder"],
|
keys: ["folder"],
|
||||||
"handler": async (folder: string) => {
|
handler: UnofficialEndpoints.openFolder,
|
||||||
// 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": {
|
"/get_symbol_defines_in_selected_code": {
|
||||||
"keys": [],
|
keys: [],
|
||||||
"handler": async () => {
|
handler: UnofficialEndpoints.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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let server: http.Server | null = null;
|
let server: http.Server | null = null;
|
||||||
export async function startRpcServer() {
|
export async function startRpcServer() {
|
||||||
server = http.createServer((req, res) => {
|
server = http.createServer((req, res) => {
|
||||||
const parsedUrl = new URL(req.url!, `http://${req.headers.host}`);
|
const parsedUrl = new URL(req.url!, `http://${req.headers.host}`);
|
||||||
logger.channel()?.info(`request: ${parsedUrl}`)
|
logger.channel()?.info(`request: ${parsedUrl}`);
|
||||||
if (parsedUrl.pathname === '/favicon.ico') {
|
if (parsedUrl.pathname === "/favicon.ico") {
|
||||||
res.writeHead(204);
|
res.writeHead(204);
|
||||||
res.end();
|
res.end();
|
||||||
return;
|
return;
|
||||||
@ -297,20 +126,23 @@ export async function startRpcServer() {
|
|||||||
|
|
||||||
let params: any = {};
|
let params: any = {};
|
||||||
|
|
||||||
if (req.method === 'POST') {
|
if (req.method === "POST") {
|
||||||
let body = '';
|
let body = "";
|
||||||
req.on('data', chunk => {
|
req.on("data", (chunk) => {
|
||||||
body += chunk.toString(); // 将Buffer转换为string
|
body += chunk.toString(); // 将Buffer转换为string
|
||||||
});
|
});
|
||||||
|
|
||||||
req.on('end', () => {
|
req.on("end", () => {
|
||||||
// 根据不同Content-Type,进行不同方式的解析
|
// 根据不同Content-Type,进行不同方式的解析
|
||||||
if (req.headers['content-type'] === 'application/json') {
|
if (req.headers["content-type"] === "application/json") {
|
||||||
// 解析JSON格式的数据
|
// 解析JSON格式的数据
|
||||||
params = JSON.parse(body);
|
params = JSON.parse(body);
|
||||||
|
|
||||||
// 处理postParams
|
// 处理postParams
|
||||||
} else if (req.headers['content-type'] === 'application/x-www-form-urlencoded') {
|
} else if (
|
||||||
|
req.headers["content-type"] ===
|
||||||
|
"application/x-www-form-urlencoded"
|
||||||
|
) {
|
||||||
// 解析URL编码的数据
|
// 解析URL编码的数据
|
||||||
params = querystring.parse(body);
|
params = querystring.parse(body);
|
||||||
// 处理postParams
|
// 处理postParams
|
||||||
@ -318,7 +150,7 @@ export async function startRpcServer() {
|
|||||||
|
|
||||||
handleRequest(parsedUrl, params, res);
|
handleRequest(parsedUrl, params, res);
|
||||||
});
|
});
|
||||||
} else if (req.method === 'GET') {
|
} else if (req.method === "GET") {
|
||||||
const queryParams = parsedUrl.searchParams;
|
const queryParams = parsedUrl.searchParams;
|
||||||
for (let param of queryParams) {
|
for (let param of queryParams) {
|
||||||
params[param[0]] = param[1];
|
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 {
|
try {
|
||||||
let responseResult = {};
|
let responseResult = {};
|
||||||
|
|
||||||
if (functionRegistry[parsedUrl.pathname]) {
|
if (functionRegistry[parsedUrl.pathname]) {
|
||||||
let keysExist = true;
|
let keysExist = true;
|
||||||
let newParameters: any[] = [];
|
let newParameters: any[] = [];
|
||||||
for (let key of functionRegistry[parsedUrl.pathname]['keys']) {
|
for (let key of functionRegistry[parsedUrl.pathname]["keys"]) {
|
||||||
if (!params.hasOwnProperty(key)) {
|
if (!params.hasOwnProperty(key)) {
|
||||||
// check whether key in functionRegistry[parsedUrl.pathname]['optional']
|
// check whether key in functionRegistry[parsedUrl.pathname]['optional']
|
||||||
newParameters.push(undefined);
|
newParameters.push(undefined);
|
||||||
@ -344,19 +180,24 @@ export async function startRpcServer() {
|
|||||||
newParameters.push(params[key]);
|
newParameters.push(params[key]);
|
||||||
}
|
}
|
||||||
if (!keysExist) {
|
if (!keysExist) {
|
||||||
responseResult['error'] = "Missing required parameters";
|
responseResult["error"] = "Missing required parameters";
|
||||||
} else {
|
} else {
|
||||||
responseResult['result'] = await functionRegistry[parsedUrl.pathname]['handler'](...newParameters);
|
responseResult["result"] = await functionRegistry[
|
||||||
if (parsedUrl.pathname === "/definitions" || parsedUrl.pathname === "/references") {
|
parsedUrl.pathname
|
||||||
responseResult = responseResult['result'];
|
]["handler"](...newParameters);
|
||||||
|
if (
|
||||||
|
parsedUrl.pathname === "/definitions" ||
|
||||||
|
parsedUrl.pathname === "/references"
|
||||||
|
) {
|
||||||
|
responseResult = responseResult["result"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
responseResult['error'] = "Function not found";
|
responseResult["error"] = "Function not found";
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// 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));
|
res.end(JSON.stringify(responseResult));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.channel()?.error(`Error: ${error}`);
|
logger.channel()?.error(`Error: ${error}`);
|
||||||
@ -367,7 +208,7 @@ export async function startRpcServer() {
|
|||||||
server.listen(0, () => {
|
server.listen(0, () => {
|
||||||
const address = server!.address();
|
const address = server!.address();
|
||||||
// `address()`返回的对象包含`port`属性,它是系统分配的端口号
|
// `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}/`);
|
logger.channel()?.info(`Server running at http://localhost:${port}/`);
|
||||||
process.env.DEVCHAT_IDE_SERVICE_URL = `http://localhost:${port}`;
|
process.env.DEVCHAT_IDE_SERVICE_URL = `http://localhost:${port}`;
|
||||||
process.env.DEVCHAT_IDE_SERVICE_PORT = `${port}`;
|
process.env.DEVCHAT_IDE_SERVICE_PORT = `${port}`;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user