add lsp bridge services
This commit is contained in:
parent
fd4d213cbc
commit
b895a6ffcf
131
src/ide_services/lsp_bridge/feature/find-defs.ts
Normal file
131
src/ide_services/lsp_bridge/feature/find-defs.ts
Normal file
@ -0,0 +1,131 @@
|
||||
import * as vscode from "vscode";
|
||||
|
||||
interface Definition {
|
||||
name: string;
|
||||
abspath: string;
|
||||
line: number; // 1-based
|
||||
character: number; // 1-based
|
||||
}
|
||||
|
||||
/**
|
||||
* @param abspath: absolute path of the file
|
||||
* @param line: line number, 1-based
|
||||
* @param character: character number, 1-based
|
||||
*
|
||||
**/
|
||||
async function findDefinitions(
|
||||
abspath: string,
|
||||
line: number,
|
||||
character: number
|
||||
): Promise<Definition[]> {
|
||||
const uri = vscode.Uri.file(abspath);
|
||||
const position = new vscode.Position(line - 1, character - 1);
|
||||
|
||||
// TODO: verify if the file & position is correct
|
||||
// const document = await vscode.workspace.openTextDocument(uri);
|
||||
|
||||
const locations = await vscode.commands.executeCommand<vscode.Location[]>(
|
||||
"vscode.executeDefinitionProvider",
|
||||
uri,
|
||||
position
|
||||
);
|
||||
|
||||
const definitions: Definition[] = [];
|
||||
if (locations) {
|
||||
for (const location of locations) {
|
||||
console.log(
|
||||
`* Definition found in file: ${location.uri.fsPath}, line: ${location.range.start.line}, character: ${location.range.start.character}`
|
||||
);
|
||||
// use `map` & `Promise.all` to improve performance if needed
|
||||
const doc = await vscode.workspace.openTextDocument(location.uri);
|
||||
|
||||
definitions.push({
|
||||
name: doc.getText(location.range),
|
||||
abspath: location.uri.fsPath,
|
||||
line: location.range.start.line + 1,
|
||||
character: location.range.start.character + 1,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.log("No definition found");
|
||||
}
|
||||
|
||||
console.log(`* Found ${definitions.length} definitions`);
|
||||
console.log(definitions);
|
||||
return definitions;
|
||||
}
|
||||
|
||||
function findText(
|
||||
document: vscode.TextDocument,
|
||||
text: string
|
||||
): vscode.Position[] {
|
||||
const positions: vscode.Position[] = [];
|
||||
if (!text) {
|
||||
return positions;
|
||||
}
|
||||
|
||||
const content = document.getText();
|
||||
|
||||
let index = content.indexOf(text);
|
||||
while (index >= 0) {
|
||||
const position = document.positionAt(index);
|
||||
positions.push(position);
|
||||
|
||||
// Find the next occurrence
|
||||
index = content.indexOf(text, index + 1);
|
||||
}
|
||||
|
||||
return positions;
|
||||
}
|
||||
|
||||
async function findDefinitionsOfToken(
|
||||
abspath: string,
|
||||
token: string
|
||||
): Promise<Definition[]> {
|
||||
const uri = vscode.Uri.file(abspath);
|
||||
const document = await vscode.workspace.openTextDocument(uri);
|
||||
const positions = findText(document, token);
|
||||
console.log(`- Found ${positions.length} <${token}>`);
|
||||
|
||||
// TODO: verify if the file & position is correct
|
||||
// const document = await vscode.workspace.openTextDocument(uri);
|
||||
const definitions: Definition[] = [];
|
||||
const visited = new Set<string>();
|
||||
|
||||
for (const position of positions) {
|
||||
const locations = await vscode.commands.executeCommand<
|
||||
vscode.Location[]
|
||||
>("vscode.executeDefinitionProvider", uri, position);
|
||||
|
||||
if (!locations) {
|
||||
console.log("No definition found");
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const location of locations) {
|
||||
const locationKey = `${location.uri.fsPath}:${location.range.start.line}:${location.range.start.character}`;
|
||||
if (visited.has(locationKey)) {
|
||||
continue;
|
||||
}
|
||||
visited.add(locationKey);
|
||||
|
||||
console.log(
|
||||
`- <${token}> Definition found in file: ${location.uri.fsPath}, line: ${location.range.start.line}, character: ${location.range.start.character}`
|
||||
);
|
||||
// use `map` & `Promise.all` to improve performance if needed
|
||||
const doc = await vscode.workspace.openTextDocument(location.uri);
|
||||
|
||||
definitions.push({
|
||||
name: doc.getText(location.range),
|
||||
abspath: location.uri.fsPath,
|
||||
line: location.range.start.line + 1,
|
||||
character: location.range.start.character + 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
console.log(`* Found ${definitions.length} definitions`);
|
||||
console.log(definitions);
|
||||
return definitions;
|
||||
}
|
||||
|
||||
export { findDefinitions, findDefinitionsOfToken };
|
59
src/ide_services/lsp_bridge/feature/find-refs.ts
Normal file
59
src/ide_services/lsp_bridge/feature/find-refs.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import * as vscode from "vscode";
|
||||
import * as path from "path";
|
||||
|
||||
// TODO: merge with find-defs.ts
|
||||
|
||||
interface Reference {
|
||||
name: string;
|
||||
abspath: string;
|
||||
line: number; // 1-based
|
||||
character: number; // 1-based
|
||||
}
|
||||
|
||||
/**
|
||||
* @param abspath: absolute path of the file
|
||||
* @param line: line number, 1-based
|
||||
* @param character: character number, 1-based
|
||||
*
|
||||
**/
|
||||
async function findReferences(
|
||||
abspath: string,
|
||||
line: number,
|
||||
character: number
|
||||
): Promise<Reference[]> {
|
||||
const uri = vscode.Uri.file(abspath);
|
||||
const position = new vscode.Position(line - 1, character - 1);
|
||||
|
||||
// TODO: verify if the file & position is correct
|
||||
// const document = await vscode.workspace.openTextDocument(uri);
|
||||
|
||||
const locations = await vscode.commands.executeCommand<vscode.Location[]>(
|
||||
"vscode.executeReferenceProvider",
|
||||
uri,
|
||||
position
|
||||
);
|
||||
|
||||
const references: Reference[] = [];
|
||||
if (locations) {
|
||||
for (const location of locations) {
|
||||
console.log(
|
||||
`* Reference found in file: ${location.uri.fsPath}, line: ${location.range.start.line}, character: ${location.range.start.character}`
|
||||
);
|
||||
// use `map` & `Promise.all` to improve performance if needed
|
||||
const doc = await vscode.workspace.openTextDocument(location.uri);
|
||||
|
||||
references.push({
|
||||
name: doc.getText(location.range),
|
||||
abspath: location.uri.fsPath,
|
||||
line: location.range.start.line + 1,
|
||||
character: location.range.start.character + 1,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.log("No reference found");
|
||||
}
|
||||
|
||||
return references;
|
||||
}
|
||||
|
||||
export { findReferences };
|
@ -10,7 +10,12 @@ 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";
|
||||
|
||||
|
||||
const functionRegistry: any = {
|
||||
@ -18,7 +23,35 @@ const functionRegistry: any = {
|
||||
"/get_lsp_brige_port": {
|
||||
"keys": [],
|
||||
"handler": async () => {
|
||||
return await UiUtilWrapper.getLSPBrigePort();
|
||||
logger.channel()?.info(`get lsp bridge port: ${process.env.DEVCHAT_IDE_SERVICE_PORT}`);
|
||||
// return await UiUtilWrapper.getLSPBrigePort();
|
||||
return process.env.DEVCHAT_IDE_SERVICE_PORT;
|
||||
}
|
||||
},
|
||||
"/definitions": {
|
||||
"keys": ["abspath", "line", "character", "token"],
|
||||
"handler": async (abspath: string, line: string|undefined = undefined, character: string|undefined = undefined, token: string|undefined = undefined) => {
|
||||
logger.channel()?.info(`get definitions: ${abspath}, ${line}, ${character}, ${token}`);
|
||||
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) => {
|
||||
logger.channel()?.info(`get references: ${abspath}, ${line}, ${character}`);
|
||||
const references = await findReferences(
|
||||
abspath,
|
||||
Number(line),
|
||||
Number(character)
|
||||
);
|
||||
|
||||
return references;
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
@ -128,6 +161,7 @@ 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();
|
||||
@ -168,30 +202,47 @@ export async function startRpcServer() {
|
||||
});
|
||||
|
||||
async function handleRequest(parsedUrl: URL, params: any, res: http.ServerResponse) {
|
||||
let responseResult = {};
|
||||
|
||||
if (functionRegistry[parsedUrl.pathname]) {
|
||||
let keysExist = true;
|
||||
let newParameters: any[] = [];
|
||||
for (let key of functionRegistry[parsedUrl.pathname]['keys']) {
|
||||
if (!params.hasOwnProperty(key)) {
|
||||
keysExist = false;
|
||||
break;
|
||||
// log current datetime
|
||||
const date = new Date();
|
||||
logger.channel()?.info(`date: ${date.toISOString()}`);
|
||||
|
||||
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'];
|
||||
}
|
||||
}
|
||||
newParameters.push(params[key]);
|
||||
}
|
||||
if (!keysExist) {
|
||||
responseResult['error'] = "Missing required parameters";
|
||||
} else {
|
||||
responseResult['result'] = await functionRegistry[parsedUrl.pathname]['handler'](...newParameters);
|
||||
responseResult['error'] = "Function not found";
|
||||
}
|
||||
} 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));
|
||||
logger.channel()?.info(`result:${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));
|
||||
|
||||
const date2 = new Date();
|
||||
logger.channel()?.info(`date finish: ${date2.toISOString()}`);
|
||||
}
|
||||
|
||||
server.listen(0, () => {
|
||||
@ -200,5 +251,6 @@ export async function startRpcServer() {
|
||||
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