feat(coder): add LSP code intelligence tools
- lsp/ module: types, config, JSON-RPC client, server-manager, operations - lsp_diagnostics: TypeScript/JavaScript diagnostics for a file - lsp_goto_definition: find symbol definition at position - lsp_find_references: find all references to a symbol - Registered as READ_TOOLS in tool index
This commit is contained in:
86
apps/coder/src/services/lsp/operations.ts
Normal file
86
apps/coder/src/services/lsp/operations.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import type { LspClient } from './client.js';
|
||||
import type { Diagnostic, Location } from './types.js';
|
||||
|
||||
function fileUri(filePath: string): string {
|
||||
return `file://${filePath.startsWith('/') ? '' : '/'}${filePath}`;
|
||||
}
|
||||
|
||||
export async function openDocument(
|
||||
client: LspClient,
|
||||
filePath: string,
|
||||
content: string,
|
||||
version: number = 1,
|
||||
): Promise<void> {
|
||||
const uri = fileUri(filePath);
|
||||
await client.notify('textDocument/didOpen', {
|
||||
textDocument: { uri, languageId: 'typescript', version, text: content },
|
||||
});
|
||||
}
|
||||
|
||||
export async function closeDocument(client: LspClient, filePath: string): Promise<void> {
|
||||
await client.notify('textDocument/didClose', {
|
||||
textDocument: { uri: fileUri(filePath) },
|
||||
});
|
||||
}
|
||||
|
||||
export async function getDiagnostics(
|
||||
client: LspClient,
|
||||
filePath: string,
|
||||
content: string,
|
||||
): Promise<Diagnostic[]> {
|
||||
const uri = fileUri(filePath);
|
||||
await openDocument(client, filePath, content);
|
||||
const result: any = await client.request('textDocument/diagnostic', {
|
||||
textDocument: { uri },
|
||||
});
|
||||
await closeDocument(client, filePath);
|
||||
const diagnostics: Diagnostic[] = [];
|
||||
if (result?.diagnostics) {
|
||||
for (const d of result.diagnostics) {
|
||||
diagnostics.push({
|
||||
range: d.range,
|
||||
severity: d.severity ?? 1,
|
||||
message: d.message,
|
||||
source: d.source,
|
||||
});
|
||||
}
|
||||
}
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
export async function gotoDefinition(
|
||||
client: LspClient,
|
||||
filePath: string,
|
||||
content: string,
|
||||
line: number,
|
||||
character: number,
|
||||
): Promise<Location | null> {
|
||||
const uri = fileUri(filePath);
|
||||
await openDocument(client, filePath, content);
|
||||
const result: any = await client.request('textDocument/definition', {
|
||||
textDocument: { uri },
|
||||
position: { line, character },
|
||||
});
|
||||
await closeDocument(client, filePath);
|
||||
if (!result) return null;
|
||||
const loc = Array.isArray(result) ? result[0] : result;
|
||||
return loc ? { uri: loc.uri, range: loc.range } : null;
|
||||
}
|
||||
|
||||
export async function findReferences(
|
||||
client: LspClient,
|
||||
filePath: string,
|
||||
content: string,
|
||||
line: number,
|
||||
character: number,
|
||||
): Promise<Location[]> {
|
||||
const uri = fileUri(filePath);
|
||||
await openDocument(client, filePath, content);
|
||||
const result: any = await client.request('textDocument/references', {
|
||||
textDocument: { uri },
|
||||
position: { line, character },
|
||||
context: { includeDeclaration: true },
|
||||
});
|
||||
await closeDocument(client, filePath);
|
||||
return (result ?? []).map((loc: any) => ({ uri: loc.uri, range: loc.range }));
|
||||
}
|
||||
Reference in New Issue
Block a user