/** * LSP API Routes * * 提供 LSP 语言服务器管理的 REST API */ import { Hono } from 'hono'; import { getConfig } from './config.js'; import type { FileDiagnostic, ServerStatus, } from '@ai-assistant/core'; import { initLSP, listServers, installServer, touchFile, getDiagnostics, getFileDiagnostics, getRunningServers, stopServer, } from '@ai-assistant/core'; export const lspRouter = new Hono(); // LSP 初始化标志 let lspInitialized = false; /** * 初始化 LSP 系统 */ function ensureLSPInitialized(): boolean { if (lspInitialized) return true; try { const config = getConfig(); initLSP(config.workdir); lspInitialized = true; console.log('[LSP] LSP module initialized'); return true; } catch (error) { console.warn('[LSP] Failed to initialize LSP module:', error); return false; } } /** * GET /lsp/servers - 获取所有语言服务器列表 */ lspRouter.get('/servers', async (c) => { if (!ensureLSPInitialized()) { return c.json( { success: false, error: 'LSP module not available', }, 503 ); } const servers = listServers(); const runningServers = new Set(getRunningServers()); // 添加运行状态 const serversWithRunningStatus = servers.map((server) => ({ ...server, running: runningServers.has(server.id) || server.languages.some(lang => runningServers.has(lang)), })); return c.json({ success: true, data: serversWithRunningStatus, }); }); /** * GET /lsp/servers/:id - 获取单个服务器详情 */ lspRouter.get('/servers/:id', async (c) => { const id = c.req.param('id'); if (!ensureLSPInitialized()) { return c.json( { success: false, error: 'LSP module not available', }, 503 ); } const servers = listServers(); const server = servers.find( (s) => s.id === id || s.displayName.toLowerCase() === id.toLowerCase() ); if (!server) { return c.json( { success: false, error: `Server not found: ${id}`, }, 404 ); } const runningServers = new Set(getRunningServers()); return c.json({ success: true, data: { ...server, running: runningServers.has(server.id) || server.languages.some(lang => runningServers.has(lang)), }, }); }); /** * POST /lsp/servers/:id/install - 安装语言服务器 */ lspRouter.post('/servers/:id/install', async (c) => { const id = c.req.param('id'); if (!ensureLSPInitialized()) { return c.json( { success: false, error: 'LSP module not available', }, 503 ); } try { const success = await installServer(id); if (success) { const servers = listServers(); const server = servers.find((s) => s.id === id); return c.json({ success: true, data: { message: `Server ${id} installed successfully`, server, }, }); } else { return c.json( { success: false, error: `Failed to install server: ${id}`, }, 500 ); } } catch (error) { return c.json( { success: false, error: error instanceof Error ? error.message : 'Installation failed', }, 500 ); } }); /** * POST /lsp/servers/:id/start - 启动语言服务器 * Body: { filePath: string } - 需要提供一个文件路径来触发对应语言的服务器 */ lspRouter.post('/servers/:id/start', async (c) => { const id = c.req.param('id'); if (!ensureLSPInitialized()) { return c.json( { success: false, error: 'LSP module not available', }, 503 ); } try { const body = await c.req.json<{ filePath?: string }>(); if (!body.filePath) { return c.json( { success: false, error: 'filePath is required to start the server', }, 400 ); } const isFirstStart = await touchFile(body.filePath); return c.json({ success: true, data: { message: `Server started for file: ${body.filePath}`, isFirstStart, runningServers: getRunningServers(), }, }); } catch (error) { return c.json( { success: false, error: error instanceof Error ? error.message : 'Start failed', }, 500 ); } }); /** * POST /lsp/servers/:id/stop - 停止语言服务器 */ lspRouter.post('/servers/:id/stop', async (c) => { const id = c.req.param('id'); if (!ensureLSPInitialized()) { return c.json( { success: false, error: 'LSP module not available', }, 503 ); } try { // id 可能是命令名或语言 ID,尝试从服务器列表找到对应的语言 const servers = listServers(); const server = servers.find( (s) => s.id === id || s.displayName.toLowerCase() === id.toLowerCase() ); // 尝试停止对应语言的服务器 let stopped = false; if (server) { for (const lang of server.languages) { const result = await stopServer(lang); if (result) { stopped = true; } } } else { // 直接尝试用 id 作为语言 ID stopped = await stopServer(id); } if (stopped) { return c.json({ success: true, data: { message: `Server ${id} stopped`, runningServers: getRunningServers(), }, }); } else { return c.json( { success: false, error: `Server ${id} is not running or could not be stopped`, }, 400 ); } } catch (error) { return c.json( { success: false, error: error instanceof Error ? error.message : 'Stop failed', }, 500 ); } }); /** * GET /lsp/running - 获取正在运行的服务器列表 */ lspRouter.get('/running', async (c) => { if (!ensureLSPInitialized()) { return c.json( { success: false, error: 'LSP module not available', }, 503 ); } return c.json({ success: true, data: getRunningServers(), }); }); /** * GET /lsp/diagnostics - 获取诊断信息 * Query: file (可选) - 指定文件路径 */ lspRouter.get('/diagnostics', async (c) => { if (!ensureLSPInitialized()) { return c.json( { success: false, error: 'LSP module not available', }, 503 ); } const file = c.req.query('file'); if (file) { // 获取单个文件的诊断信息 const diagnostics = getFileDiagnostics(file); return c.json({ success: true, data: { file, diagnostics, summary: { totalErrors: diagnostics.filter((d) => d.severity === 'error').length, totalWarnings: diagnostics.filter((d) => d.severity === 'warning').length, }, }, }); } // 获取所有文件的诊断信息 const allDiagnostics = getDiagnostics(); const files: Array<{ file: string; diagnostics: FileDiagnostic[] }> = []; let totalErrors = 0; let totalWarnings = 0; for (const [filePath, diagnostics] of allDiagnostics) { files.push({ file: filePath, diagnostics }); totalErrors += diagnostics.filter((d) => d.severity === 'error').length; totalWarnings += diagnostics.filter((d) => d.severity === 'warning').length; } return c.json({ success: true, data: { files, summary: { totalFiles: files.length, totalErrors, totalWarnings, }, }, }); });