Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,12 @@
"description": "The XML Language server allows other VSCode extensions to extend its functionality. It requires Java-specific features in order to do this. If extensions to the XML language server are detected, but a binary XML language server is run, a warning will appear. This setting can be used to disable this warning.",
"scope": "window"
},
"xml.mcp.enabled": {
"type": "boolean",
"default": false,
"markdownDescription": "Enable multi-client mode. When enabled, the XML language server listens on a TCP socket and registers itself in `${workspace}/.lsp-servers/lemminx.json`, allowing MCP servers (like languagetools) to connect and share the same server instance. Requires restart to take effect.",
"scope": "window"
},
"xml.server.binary.path": {
"type": "string",
"description": "Specify the path of a custom binary version of the XML server to use. A binary will be downloaded if this is not set.",
Expand Down
6 changes: 3 additions & 3 deletions src/client/xmlClient.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { TelemetryEvent } from '@redhat-developer/vscode-redhat-telemetry/lib';
import { commands, ExtensionContext, extensions, Position, TextDocument, TextEditor, Uri, window, workspace } from 'vscode';
import { Command, ConfigurationParams, ConfigurationRequest, DidChangeConfigurationNotification, DocumentFilter, DocumentSelector, ExecuteCommandParams, LanguageClientOptions, MessageType, NotificationType, RequestType, RevealOutputChannelOn, State, TextDocumentPositionParams } from "vscode-languageclient";

Check warning on line 3 in src/client/xmlClient.ts

View workflow job for this annotation

GitHub Actions / lint

'DocumentSelector' is defined but never used

Check warning on line 3 in src/client/xmlClient.ts

View workflow job for this annotation

GitHub Actions / lint

'DocumentSelector' is defined but never used
import { Executable, LanguageClient } from 'vscode-languageclient/node';
import { LanguageClient, ServerOptions } from 'vscode-languageclient/node';
import { XMLFileAssociation } from '../api/xmlExtensionApi';
import { registerClientServerCommands } from '../commands/registerCommands';
import * as ServerCommandConstants from '../commands/serverCommandConstants';
Expand All @@ -26,14 +26,14 @@
return l as string;
});

const ExecuteClientCommandRequest: RequestType<ExecuteCommandParams, any, void> = new RequestType('xml/executeClientCommand');

Check warning on line 29 in src/client/xmlClient.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type

Check warning on line 29 in src/client/xmlClient.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type

const TagCloseRequest: RequestType<TextDocumentPositionParams, AutoCloseResult, any> = new RequestType('xml/closeTag');

Check warning on line 31 in src/client/xmlClient.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type

Check warning on line 31 in src/client/xmlClient.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type

interface ActionableMessage {
severity: MessageType;
message: string;
data?: any;

Check warning on line 36 in src/client/xmlClient.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type

Check warning on line 36 in src/client/xmlClient.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
commands?: Command[];
}

Expand All @@ -41,10 +41,10 @@

let languageClient: LanguageClient;

export async function startLanguageClient(context: ExtensionContext, executable: Executable, logfile: string, externalXmlSettings: ExternalXmlSettings, requirementsData: RequirementsData): Promise<LanguageClient> {
export async function startLanguageClient(context: ExtensionContext, serverOptions: ServerOptions, logfile: string, externalXmlSettings: ExternalXmlSettings, requirementsData: RequirementsData): Promise<LanguageClient> {

const languageClientOptions: LanguageClientOptions = getLanguageClientOptions(logfile, externalXmlSettings, requirementsData, context);
languageClient = new LanguageClient('xml', 'XML Support', executable, languageClientOptions);
languageClient = new LanguageClient('xml', 'XML Support', serverOptions, languageClientOptions);
//In vscode-languageclient version 9.0.0, inline completion (textDocument/inlineCompletion) is a proposed feature
// TODO remove registerProposedFeatures once upgraded to 10.x
languageClient.registerProposedFeatures();
Expand Down Expand Up @@ -97,7 +97,7 @@
// Copied from:
// https://github.com/redhat-developer/vscode-java/pull/1081/files
languageClient.onRequest(ConfigurationRequest.type, (params: ConfigurationParams) => {
const result: any[] = [];

Check warning on line 100 in src/client/xmlClient.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type

Check warning on line 100 in src/client/xmlClient.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
const activeEditor: TextEditor | undefined = window.activeTextEditor;
for (const item of params.items) {
if (activeEditor && activeEditor.document.uri.toString() === Uri.parse(item.scopeUri).toString()) {
Expand All @@ -122,7 +122,7 @@
}
}));

const onDidGrantWorkspaceTrust = (workspace as any).onDidGrantWorkspaceTrust;

Check warning on line 125 in src/client/xmlClient.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type

Check warning on line 125 in src/client/xmlClient.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
if (onDidGrantWorkspaceTrust !== undefined) {
context.subscriptions.push(onDidGrantWorkspaceTrust(() => {
languageClient.sendNotification(DidChangeConfigurationNotification.type, { settings: getXMLSettings(requirementsData.java_home, logfile, externalXmlSettings) });
Expand Down Expand Up @@ -198,7 +198,7 @@
show(notification.message, ...titles).then((selection) => {
for (const action of notification.commands) {
if (action.title === selection) {
const args: any[] = (action.arguments) ? action.arguments : [];

Check warning on line 201 in src/client/xmlClient.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type

Check warning on line 201 in src/client/xmlClient.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
commands.executeCommand(action.command, ...args);
break;
}
Expand Down
4 changes: 2 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import * as fs from 'fs-extra';
import { ConfigurationTarget, ExtensionContext, Uri, commands, extensions, languages, window, workspace } from "vscode";
import { Executable, LanguageClient } from 'vscode-languageclient/node';
import { Executable, LanguageClient, ServerOptions } from 'vscode-languageclient/node';

Check warning on line 15 in src/extension.ts

View workflow job for this annotation

GitHub Actions / lint

'Executable' is defined but never used

Check warning on line 15 in src/extension.ts

View workflow job for this annotation

GitHub Actions / lint

'Executable' is defined but never used
import { XMLExtensionApi } from './api/xmlExtensionApi';
import { getXmlExtensionApiImplementation } from './api/xmlExtensionApiImplementation';
import { cleanUpHeapDumps } from './client/clientErrorHandler';
Expand Down Expand Up @@ -67,7 +67,7 @@

const externalXmlSettings: ExternalXmlSettings = new ExternalXmlSettings();

const serverOptions: Executable = await prepareExecutable(
const serverOptions: ServerOptions = await prepareExecutable(
requirementsData, collectXmlJavaExtensions(extensions.all, getXMLConfiguration().get("extension.jars", [])), context);

languageClient = await startLanguageClient(context, serverOptions, logfile, externalXmlSettings, requirementsData);
Expand Down
22 changes: 19 additions & 3 deletions src/server/java/javaServerStarter.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as os from 'os';
import * as path from 'path';
import { ExtensionContext, window, workspace } from 'vscode';
import { env, ExtensionContext, version as vscodeVersion, window, workspace } from 'vscode';
import { Executable } from 'vscode-languageclient/node';
import { getProxySettings, getProxySettingsAsJVMArgs, jvmArgsContainsProxySettings, ProxySettings } from '../../settings/proxySettings';
import { getJavaagentFlag, getKey, getXMLConfiguration, IS_WORKSPACE_VMARGS_XML_ALLOWED, xmlServerVmargs } from '../../settings/settings';
Expand All @@ -19,13 +19,17 @@ export async function prepareJavaExecutable(
xmlJavaExtensions: string[]
): Promise<Executable> {

const mcpEnabled = getXMLConfiguration().get('mcp.enabled', false);
const workspacePath = workspace.workspaceFolders?.[0]?.uri.fsPath || '';

// Always use stdio mode, but pass MCP parameters if enabled
return {
command: path.resolve(requirements.java_home + '/bin/java'),
args: prepareParams(requirements, xmlJavaExtensions, context)
args: prepareParams(requirements, xmlJavaExtensions, context, mcpEnabled, workspacePath)
} as Executable;
}

function prepareParams(requirements: RequirementsData, xmlJavaExtensions: string[], context: ExtensionContext): string[] {
function prepareParams(requirements: RequirementsData, xmlJavaExtensions: string[], context: ExtensionContext, mcpEnabled?: boolean, workspacePath?: string): string[] {
const params: string[] = [];
if (DEBUG) {
if (process.env['SUSPEND_SERVER'] === 'true') {
Expand Down Expand Up @@ -96,7 +100,17 @@ function prepareParams(requirements: RequirementsData, xmlJavaExtensions: string
xmlJavaExtensionsClasspath = pathSeparator + xmlJavaExtensions.join(pathSeparator);
}
params.push('-cp'); params.push(path.resolve(server_home, launchersFound[0]) + xmlJavaExtensionsClasspath);

// Always use stdio launcher
params.push('org.eclipse.lemminx.XMLServerLauncher');

// Add MCP arguments if enabled
if (mcpEnabled && workspacePath) {
params.push('--mcp-enabled');
params.push('--workspace'); params.push(workspacePath);
params.push('--client-name'); params.push(env.appName); // e.g., "Visual Studio Code", "VSCodium"
params.push('--client-version'); params.push(vscodeVersion);
}
} else {
return null;
}
Expand Down Expand Up @@ -135,3 +149,5 @@ export function parseVMargs(params: any[], vmargsLine: string) {
}
});
}


8 changes: 4 additions & 4 deletions src/server/serverStarter.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import { commands, ConfigurationTarget, ExtensionContext, window } from "vscode";
import { Executable } from "vscode-languageclient/node";
import { Executable, ServerOptions } from "vscode-languageclient/node";
import { getXMLConfiguration } from "../settings/settings";
import * as Telemetry from "../telemetry";
import { ABORTED_ERROR, prepareBinaryExecutable } from "./binary/binaryServerStarter";
import { prepareJavaExecutable } from "./java/javaServerStarter";
import { getOpenJDKDownloadLink, RequirementsData } from "./requirements";

/**
* Returns the executable to use to launch LemMinX (the XML Language Server)
* Returns the server options to use to launch LemMinX (the XML Language Server)
*
* @param requirements the java information, or an empty object if there is no java
* @param xmlJavaExtensions a list of all the java extension jars
* @param context the extensions context
* @throws if neither the binary nor the java version of the extension can be launched
* @returns the executable to launch LemMinX with (the XML language server)
* @returns the server options to launch LemMinX with (the XML language server)
*/
export async function prepareExecutable(
requirements: RequirementsData,
xmlJavaExtensions: string[],
context: ExtensionContext): Promise<Executable> {
context: ExtensionContext): Promise<ServerOptions> {

const hasJava: boolean = requirements.java_home !== undefined;
const hasExtensions: boolean = xmlJavaExtensions.length !== 0;
Expand Down
Loading