mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-30 20:50:34 +00:00
feat(lsp): Removes built-in LSP configuration options and improves configuration loading mechanism
- remove configuration options such as lsp.enabled, lsp.allowed, lsp.excluded, etc. from settings.json schema - Delete lspSettingsSchema.ts files and associated JSON schema definitions - Removed VS Code settings loading function, no longer merge. vscode/settings.json configuration - Updated LSP documentation to reflect new configurations and experimental flags -remove allow/exclude parameters in NativeLspService constructor - Create new LspConfigLoader classes to handle LSP configuration loading and merging - Updated debug guide to match the new configuration mechanism - Simplify loadCliConfig functions, remove startLsp options - Reconstruct the configuration loading process to remove duplicate configuration merge logic - Add LspConfigLoader classes to implement configuration parsing and merging functions
This commit is contained in:
parent
45e947dcbc
commit
8420386d14
33 changed files with 3064 additions and 3907 deletions
|
|
@ -600,42 +600,17 @@ describe('loadCliConfig', () => {
|
|||
it('should initialize native LSP service when enabled', async () => {
|
||||
process.argv = ['node', 'script.js', '--experimental-lsp'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = {
|
||||
lsp: {
|
||||
allowed: ['typescript-language-server'],
|
||||
excluded: ['pylsp'],
|
||||
},
|
||||
};
|
||||
const settings: Settings = {};
|
||||
|
||||
const config = await loadCliConfig(settings, argv);
|
||||
|
||||
// LSP is enabled via --experimental-lsp flag
|
||||
expect(config.isLspEnabled()).toBe(true);
|
||||
expect(config.getLspAllowed()).toEqual(['typescript-language-server']);
|
||||
expect(config.getLspExcluded()).toEqual(['pylsp']);
|
||||
expect(nativeLspServiceMock).toHaveBeenCalledTimes(1);
|
||||
const lspInstance = getLastLspInstance();
|
||||
expect(lspInstance).toBeDefined();
|
||||
expect(lspInstance?.discoverAndPrepare).toHaveBeenCalledTimes(1);
|
||||
expect(lspInstance?.start).toHaveBeenCalledTimes(1);
|
||||
|
||||
const options = nativeLspServiceMock.mock.calls[0][5];
|
||||
expect(options?.allowedServers).toEqual(['typescript-language-server']);
|
||||
expect(options?.excludedServers).toEqual(['pylsp']);
|
||||
});
|
||||
|
||||
it('should skip native LSP startup when startLsp option is false', async () => {
|
||||
process.argv = ['node', 'script.js', '--experimental-lsp'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = {};
|
||||
|
||||
const config = await loadCliConfig(settings, argv, undefined, undefined, {
|
||||
startLsp: false,
|
||||
});
|
||||
|
||||
expect(config.isLspEnabled()).toBe(true);
|
||||
expect(nativeLspServiceMock).not.toHaveBeenCalled();
|
||||
expect(getLastLspInstance()).toBeUndefined();
|
||||
});
|
||||
|
||||
describe('Proxy configuration', () => {
|
||||
|
|
|
|||
|
|
@ -151,14 +151,6 @@ export interface CliArgs {
|
|||
channel: string | undefined;
|
||||
}
|
||||
|
||||
export interface LoadCliConfigOptions {
|
||||
/**
|
||||
* Whether to start the native LSP service during config load.
|
||||
* Disable when doing preflight runs (e.g., sandbox preparation).
|
||||
*/
|
||||
startLsp?: boolean;
|
||||
}
|
||||
|
||||
class NativeLspClient implements LspClient {
|
||||
constructor(private readonly service: NativeLspService) {}
|
||||
|
||||
|
|
@ -819,7 +811,6 @@ export async function loadCliConfig(
|
|||
argv: CliArgs,
|
||||
cwd: string = process.cwd(),
|
||||
overrideExtensions?: string[],
|
||||
options: LoadCliConfigOptions = {},
|
||||
): Promise<Config> {
|
||||
const debugMode = isDebugMode(argv);
|
||||
|
||||
|
|
@ -877,9 +868,6 @@ export async function loadCliConfig(
|
|||
|
||||
// LSP configuration: enabled only via --experimental-lsp flag
|
||||
const lspEnabled = argv.experimentalLsp === true;
|
||||
const lspAllowed = settings.lsp?.allowed ?? settings.mcp?.allowed;
|
||||
const lspExcluded = settings.lsp?.excluded ?? settings.mcp?.excluded;
|
||||
const lspLanguageServers = settings.lsp?.languageServers;
|
||||
let lspClient: LspClient | undefined;
|
||||
const question = argv.promptInteractive || argv.prompt || '';
|
||||
const inputFormat: InputFormat =
|
||||
|
|
@ -1186,13 +1174,10 @@ export async function loadCliConfig(
|
|||
argv.chatRecording ?? settings.general?.chatRecording ?? true,
|
||||
lsp: {
|
||||
enabled: lspEnabled,
|
||||
allowed: lspAllowed,
|
||||
excluded: lspExcluded,
|
||||
},
|
||||
});
|
||||
|
||||
const shouldStartLsp = options.startLsp ?? true;
|
||||
if (shouldStartLsp && lspEnabled) {
|
||||
if (lspEnabled) {
|
||||
try {
|
||||
const lspService = new NativeLspService(
|
||||
config,
|
||||
|
|
@ -1201,10 +1186,7 @@ export async function loadCliConfig(
|
|||
fileService,
|
||||
ideContextStore,
|
||||
{
|
||||
allowedServers: lspAllowed,
|
||||
excludedServers: lspExcluded,
|
||||
requireTrustedWorkspace: folderTrust,
|
||||
inlineServerConfigs: lspLanguageServers,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
import type { JSONSchema7 } from 'json-schema';
|
||||
|
||||
export const lspSettingsSchema: JSONSchema7 = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
'lsp.enabled': {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description:
|
||||
'启用 LSP 语言服务器协议支持(实验性功能)。必须通过 --experimental-lsp 命令行参数显式开启。'
|
||||
},
|
||||
'lsp.allowed': {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string'
|
||||
},
|
||||
default: [],
|
||||
description: '允许运行的 LSP 服务器列表'
|
||||
},
|
||||
'lsp.excluded': {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string'
|
||||
},
|
||||
default: [],
|
||||
description: '禁止运行的 LSP 服务器列表'
|
||||
},
|
||||
'lsp.autoDetect': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: '自动检测项目语言并启动相应 LSP 服务器'
|
||||
},
|
||||
'lsp.serverTimeout': {
|
||||
type: 'number',
|
||||
default: 10000,
|
||||
description: 'LSP 服务器启动超时时间(毫秒)'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -150,39 +150,6 @@ export function getSystemDefaultsPath(): string {
|
|||
);
|
||||
}
|
||||
|
||||
function getVsCodeSettingsPath(workspaceDir: string): string {
|
||||
return path.join(workspaceDir, '.vscode', 'settings.json');
|
||||
}
|
||||
|
||||
function loadVsCodeSettings(workspaceDir: string): Settings {
|
||||
const vscodeSettingsPath = getVsCodeSettingsPath(workspaceDir);
|
||||
try {
|
||||
if (fs.existsSync(vscodeSettingsPath)) {
|
||||
const content = fs.readFileSync(vscodeSettingsPath, 'utf-8');
|
||||
const rawSettings: unknown = JSON.parse(stripJsonComments(content));
|
||||
|
||||
if (
|
||||
typeof rawSettings !== 'object' ||
|
||||
rawSettings === null ||
|
||||
Array.isArray(rawSettings)
|
||||
) {
|
||||
console.error(
|
||||
`VS Code settings file is not a valid JSON object: ${vscodeSettingsPath}`,
|
||||
);
|
||||
return {};
|
||||
}
|
||||
|
||||
return rawSettings as Settings;
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
console.error(
|
||||
`Error loading VS Code settings from ${vscodeSettingsPath}:`,
|
||||
getErrorMessage(error),
|
||||
);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
export type { DnsResolutionOrder } from './settingsSchema.js';
|
||||
|
||||
export enum SettingScope {
|
||||
|
|
@ -746,9 +713,6 @@ export function loadSettings(
|
|||
workspaceDir,
|
||||
).getWorkspaceSettingsPath();
|
||||
|
||||
// Load VS Code settings as an additional source of configuration
|
||||
const vscodeSettings = loadVsCodeSettings(workspaceDir);
|
||||
|
||||
const loadAndMigrate = (
|
||||
filePath: string,
|
||||
scope: SettingScope,
|
||||
|
|
@ -853,14 +817,6 @@ export function loadSettings(
|
|||
userSettings = resolveEnvVarsInObject(userResult.settings);
|
||||
workspaceSettings = resolveEnvVarsInObject(workspaceResult.settings);
|
||||
|
||||
// Merge VS Code settings into workspace settings (VS Code settings take precedence)
|
||||
workspaceSettings = customDeepMerge(
|
||||
getMergeStrategyForPath,
|
||||
{},
|
||||
workspaceSettings,
|
||||
vscodeSettings,
|
||||
) as Settings;
|
||||
|
||||
// Support legacy theme names
|
||||
if (userSettings.ui?.theme === 'VS') {
|
||||
userSettings.ui.theme = DefaultLight.name;
|
||||
|
|
@ -874,13 +830,11 @@ export function loadSettings(
|
|||
}
|
||||
|
||||
// For the initial trust check, we can only use user and system settings.
|
||||
// We also include VS Code settings as they may contain trust-related settings
|
||||
const initialTrustCheckSettings = customDeepMerge(
|
||||
getMergeStrategyForPath,
|
||||
{},
|
||||
systemSettings,
|
||||
userSettings,
|
||||
vscodeSettings, // Include VS Code settings
|
||||
);
|
||||
const isTrusted =
|
||||
isWorkspaceTrusted(initialTrustCheckSettings as Settings).isTrusted ?? true;
|
||||
|
|
@ -894,18 +848,9 @@ export function loadSettings(
|
|||
isTrusted,
|
||||
);
|
||||
|
||||
// Add VS Code settings to the temp merged settings for environment loading
|
||||
// Since loadEnvironment depends on settings, we need to consider VS Code settings as well
|
||||
const tempMergedSettingsWithVsCode = customDeepMerge(
|
||||
getMergeStrategyForPath,
|
||||
{},
|
||||
tempMergedSettings,
|
||||
vscodeSettings,
|
||||
) as Settings;
|
||||
|
||||
// loadEnviroment depends on settings so we have to create a temp version of
|
||||
// the settings to avoid a cycle
|
||||
loadEnvironment(tempMergedSettingsWithVsCode);
|
||||
loadEnvironment(tempMergedSettings);
|
||||
|
||||
// Create LoadedSettings first
|
||||
|
||||
|
|
|
|||
|
|
@ -967,59 +967,6 @@ const SETTINGS_SCHEMA = {
|
|||
},
|
||||
},
|
||||
},
|
||||
lsp: {
|
||||
type: 'object',
|
||||
label: 'LSP',
|
||||
category: 'LSP',
|
||||
requiresRestart: true,
|
||||
default: {},
|
||||
description:
|
||||
'Settings for the native Language Server Protocol integration. Enable with --experimental-lsp flag.',
|
||||
showInDialog: false,
|
||||
properties: {
|
||||
enabled: {
|
||||
type: 'boolean',
|
||||
label: 'Enable LSP',
|
||||
category: 'LSP',
|
||||
requiresRestart: true,
|
||||
default: false,
|
||||
description:
|
||||
'Enable the native LSP client. Prefer using --experimental-lsp command line flag instead.',
|
||||
showInDialog: false,
|
||||
},
|
||||
allowed: {
|
||||
type: 'array',
|
||||
label: 'Allow LSP Servers',
|
||||
category: 'LSP',
|
||||
requiresRestart: true,
|
||||
default: undefined as string[] | undefined,
|
||||
description:
|
||||
'Optional allowlist of LSP server names. If set, only matching servers will start.',
|
||||
showInDialog: false,
|
||||
},
|
||||
excluded: {
|
||||
type: 'array',
|
||||
label: 'Exclude LSP Servers',
|
||||
category: 'LSP',
|
||||
requiresRestart: true,
|
||||
default: undefined as string[] | undefined,
|
||||
description:
|
||||
'Optional blocklist of LSP server names that should not start.',
|
||||
showInDialog: false,
|
||||
},
|
||||
languageServers: {
|
||||
type: 'object',
|
||||
label: 'LSP Language Servers',
|
||||
category: 'LSP',
|
||||
requiresRestart: true,
|
||||
default: {} as Record<string, unknown>,
|
||||
description:
|
||||
'Inline LSP server configuration (same format as .lsp.json).',
|
||||
showInDialog: false,
|
||||
mergeStrategy: MergeStrategy.SHALLOW_MERGE,
|
||||
},
|
||||
},
|
||||
},
|
||||
useSmartEdit: {
|
||||
type: 'boolean',
|
||||
label: 'Use Smart Edit',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue