import type { PluginError } from '../../types/plugin.js' import { logForDebugging } from '../../utils/debug.js' import { errorMessage, toError } from '../../utils/errors.js' import { logError } from '../../utils/log.js' import { getPluginLspServers } from '../../utils/plugins/lspPluginIntegration.js' import { loadAllPluginsCacheOnly } from '../../utils/plugins/pluginLoader.js' import type { ScopedLspServerConfig } from './types.js' /** * Get all configured LSP servers from plugins. * LSP servers are only supported via plugins, not user/project settings. * * @returns Object containing servers configuration keyed by scoped server name */ export async function getAllLspServers(): Promise<{ servers: Record }> { const allServers: Record = {} try { // Get all enabled plugins const { enabled: plugins } = await loadAllPluginsCacheOnly() // Load LSP servers from each plugin in parallel. // Each plugin is independent — results are merged in original order so // Object.assign collision precedence (later plugins win) is preserved. const results = await Promise.all( plugins.map(async plugin => { const errors: PluginError[] = [] try { const scopedServers = await getPluginLspServers(plugin, errors) return { plugin, scopedServers, errors } } catch (e) { // Defensive: if one plugin throws, don't lose results from the // others. The previous serial loop implicitly tolerated this. logForDebugging( `Failed to load LSP servers for plugin ${plugin.name}: ${e}`, { level: 'error' }, ) return { plugin, scopedServers: undefined, errors } } }), ) for (const { plugin, scopedServers, errors } of results) { const serverCount = scopedServers ? Object.keys(scopedServers).length : 0 if (serverCount > 0) { // Merge into all servers (already scoped by getPluginLspServers) Object.assign(allServers, scopedServers) logForDebugging( `Loaded ${serverCount} LSP server(s) from plugin: ${plugin.name}`, ) } // Log any errors encountered if (errors.length > 0) { logForDebugging( `${errors.length} error(s) loading LSP servers from plugin: ${plugin.name}`, ) } } logForDebugging( `Total LSP servers loaded: ${Object.keys(allServers).length}`, ) } catch (error) { // Log error for monitoring production issues. // LSP is optional, so we don't throw - but we need visibility // into why plugin loading fails to improve the feature. logError(toError(error)) logForDebugging(`Error loading LSP servers: ${errorMessage(error)}`) } return { servers: allServers, } }