Skip to content

Commit

Permalink
Run an executable version of LemMinX
Browse files Browse the repository at this point in the history
Download a binary lemminx, check its integrity, then run it.

Includes:
 * Setting to specify a binary, `xml.server.binary.path`
 * Setting to specify args for the binary, `xml.server.binary.args`

Defaults to java server in cases such as:
 * The binary can't be downloaded
 * The file containing the expected hash of the binary is missing
 * The hash of the binary doesn't match the expected hash
 * Binary specified in setting can't be located and the above three fail

Signed-off-by: David Thompson <[email protected]>
  • Loading branch information
datho7561 committed Oct 13, 2020
1 parent 88c2b0e commit 8f8af65
Show file tree
Hide file tree
Showing 3 changed files with 298 additions and 179 deletions.
16 changes: 16 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,22 @@
"markdownDescription": "Set a custom folder path for cached XML Schemas. An absolute path is expected, although the `~` prefix (for the user home directory) is supported. Default is `~/.lemminx`. Please refer to the [cache documentation](command:xml.open.docs?%5B%7B%22page%22%3A%22Preferences%22%2C%22section%22%3A%22server-cache-path%22%7D%5D) for more information.",
"scope": "window"
},
"xml.server.binary.enabled": {
"type": "boolean",
"default": false,
"description": "Use an experimental binary build of the LemMinX XML language server instead of the Java version. Additions to LemMinX provided by other extensions won't work.",
"scope": "window"
},
"xml.server.binary.path": {
"type": "string",
"description": "The path to the server binary to run. Will be ignored if `xml.server.binary.enabled` is not set. A binary will be downloaded if this is not set.",
"scope": "machine"
},
"xml.server.binary.args": {
"type": "string",
"markdownDescription": "Command line arguments to supply to the server binary when the server binary is being used. Takes into effect after relaunching VSCode. Please refer to [this website for the available options](https://www.graalvm.org/reference-manual/native-image/HostedvsRuntimeOptions/). For example, you can increase the maximum memory that the server can use to 1 GB by adding `-Xmx1g`",
"scope": "machine"
},
"xml.trace.server": {
"type": "string",
"enum": [
Expand Down
286 changes: 136 additions & 150 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,17 @@
* Microsoft Corporation - Auto Closing Tags
*/

import { prepareExecutable } from './javaServerStarter';
import {
LanguageClientOptions,
RevealOutputChannelOn,
LanguageClient,
DidChangeConfigurationNotification,
RequestType,
TextDocumentPositionParams,
ReferencesRequest,
NotificationType,
MessageType,
ConfigurationRequest,
ConfigurationParams,
ExecuteCommandParams,
CancellationToken,
ExecuteCommandRequest
} from 'vscode-languageclient';
import * as requirements from './requirements';
import { languages, IndentAction, workspace, window, commands, ExtensionContext, TextDocument, Position, LanguageConfiguration, Uri, extensions, Command, TextEditor } from "vscode";
import * as path from 'path';
import * as os from 'os';
import { activateTagClosing, AutoCloseResult } from './tagClosing';
import * as path from 'path';
import { Command, commands, ExtensionContext, extensions, IndentAction, LanguageConfiguration, languages, Position, TextDocument, TextEditor, Uri, window, workspace } from "vscode";
import { CancellationToken, ConfigurationParams, ConfigurationRequest, DidChangeConfigurationNotification, Executable, ExecuteCommandParams, ExecuteCommandRequest, LanguageClient, LanguageClientOptions, MessageType, NotificationType, ReferencesRequest, RequestType, RevealOutputChannelOn, TextDocumentPositionParams } from 'vscode-languageclient';
import { Commands } from './commands';
import { getXMLConfiguration, onConfigurationChange, subscribeJDKChangeConfiguration } from './settings';
import { collectXmlJavaExtensions, onExtensionChange } from './plugin';
import { prepareExecutable } from './javaServerStarter';
import { markdownPreviewProvider } from "./markdownPreviewProvider";
import { collectXmlJavaExtensions, onExtensionChange } from './plugin';
import * as requirements from './requirements';
import { getXMLConfiguration, onConfigurationChange, subscribeJDKChangeConfiguration } from './settings';
import { activateTagClosing, AutoCloseResult } from './tagClosing';

export interface ScopeInfo {
scope: "default" | "global" | "workspace" | "folder";
Expand Down Expand Up @@ -231,144 +216,145 @@ export function activate(context: ExtensionContext) {
}
}

let serverOptions = prepareExecutable(requirements, collectXmlJavaExtensions(extensions.all, getXMLConfiguration().get("extension.jars", [])), context);
languageClient = new LanguageClient('xml', 'XML Support', serverOptions, clientOptions);
let toDispose = context.subscriptions;
let disposable = languageClient.start();
toDispose.push(disposable);

languages.setLanguageConfiguration('xml', getIndentationRules());
languages.setLanguageConfiguration('xsl', getIndentationRules());

return languageClient.onReady().then(() => {
//Detect JDK configuration changes
disposable = subscribeJDKChangeConfiguration();
prepareExecutable(requirements, collectXmlJavaExtensions(extensions.all, getXMLConfiguration().get("extension.jars", [])), context).then((serverOptions: Executable) => {
languageClient = new LanguageClient('xml', 'XML Support', serverOptions, clientOptions);
let toDispose = context.subscriptions;
let disposable = languageClient.start();
toDispose.push(disposable);

// Code Lens actions
context.subscriptions.push(commands.registerCommand(Commands.SHOW_REFERENCES, (uriString: string, position: Position) => {
const uri = Uri.parse(uriString);
workspace.openTextDocument(uri).then(document => {
// Consume references service from the XML Language Server
let param = languageClient.code2ProtocolConverter.asTextDocumentPositionParams(document, position);
languageClient.sendRequest(ReferencesRequest.type, param).then(locations => {
commands.executeCommand(Commands.EDITOR_SHOW_REFERENCES, uri, languageClient.protocol2CodeConverter.asPosition(position), locations.map(languageClient.protocol2CodeConverter.asLocation));
languages.setLanguageConfiguration('xml', getIndentationRules());
languages.setLanguageConfiguration('xsl', getIndentationRules());

return languageClient.onReady().then(() => {
//Detect JDK configuration changes
disposable = subscribeJDKChangeConfiguration();
toDispose.push(disposable);

// Code Lens actions
context.subscriptions.push(commands.registerCommand(Commands.SHOW_REFERENCES, (uriString: string, position: Position) => {
const uri = Uri.parse(uriString);
workspace.openTextDocument(uri).then(document => {
// Consume references service from the XML Language Server
let param = languageClient.code2ProtocolConverter.asTextDocumentPositionParams(document, position);
languageClient.sendRequest(ReferencesRequest.type, param).then(locations => {
commands.executeCommand(Commands.EDITOR_SHOW_REFERENCES, uri, languageClient.protocol2CodeConverter.asPosition(position), locations.map(languageClient.protocol2CodeConverter.asLocation));
})
})
})
}));

setupActionableNotificationListener(languageClient);

// Handler for 'xml/executeClientCommand` request message that executes a command on the client
languageClient.onRequest(ExecuteClientCommandRequest.type, async (params: ExecuteCommandParams) => {
return await commands.executeCommand(params.command, ...params.arguments);
});

// Register client command to execute custom XML Language Server command
context.subscriptions.push(commands.registerCommand(Commands.EXECUTE_WORKSPACE_COMMAND, (command, ...rest) => {
let token: CancellationToken;
let commandArgs: any[] = rest;
if (rest && rest.length && CancellationToken.is(rest[rest.length - 1])) {
token = rest[rest.length - 1];
commandArgs = rest.slice(0, rest.length - 1);
}
const params: ExecuteCommandParams = {
command,
arguments: commandArgs
};
if (token) {
return languageClient.sendRequest(ExecuteCommandRequest.type, params, token);
} else {
return languageClient.sendRequest(ExecuteCommandRequest.type, params);
}
}));

context.subscriptions.push(commands.registerCommand(Commands.OPEN_SETTINGS, async (settingId?: string) => {
commands.executeCommand('workbench.action.openSettings', settingId);
}));

// Setup autoCloseTags
const tagProvider = (document: TextDocument, position: Position) => {
let param = languageClient.code2ProtocolConverter.asTextDocumentPositionParams(document, position);
let text = languageClient.sendRequest(TagCloseRequest.type, param);
return text;
};
context.subscriptions.push(activateTagClosing(tagProvider, { xml: true, xsl: true }, Commands.AUTO_CLOSE_TAGS));

if (extensions.onDidChange) {// Theia doesn't support this API yet
context.subscriptions.push(extensions.onDidChange(() => {
onExtensionChange(extensions.all, getXMLConfiguration().get("extension.jars", []));
}));
}

// Copied from:
// https://github.com/redhat-developer/vscode-java/pull/1081/files
languageClient.onRequest(ConfigurationRequest.type, (params: ConfigurationParams) => {
const result: any[] = [];
const activeEditor: TextEditor | undefined = window.activeTextEditor;
for (const item of params.items) {
if (activeEditor && activeEditor.document.uri.toString() === Uri.parse(item.scopeUri).toString()) {
if (item.section === "xml.format.insertSpaces") {
result.push(activeEditor.options.insertSpaces);
} else if (item.section === "xml.format.tabSize") {
result.push(activeEditor.options.tabSize);
}
setupActionableNotificationListener(languageClient);

// Handler for 'xml/executeClientCommand` request message that executes a command on the client
languageClient.onRequest(ExecuteClientCommandRequest.type, async (params: ExecuteCommandParams) => {
return await commands.executeCommand(params.command, ...params.arguments);
});

// Register client command to execute custom XML Language Server command
context.subscriptions.push(commands.registerCommand(Commands.EXECUTE_WORKSPACE_COMMAND, (command, ...rest) => {
let token: CancellationToken;
let commandArgs: any[] = rest;
if (rest && rest.length && CancellationToken.is(rest[rest.length - 1])) {
token = rest[rest.length - 1];
commandArgs = rest.slice(0, rest.length - 1);
}
const params: ExecuteCommandParams = {
command,
arguments: commandArgs
};
if (token) {
return languageClient.sendRequest(ExecuteCommandRequest.type, params, token);
} else {
result.push(workspace.getConfiguration(null, Uri.parse(item.scopeUri)).get(item.section));
return languageClient.sendRequest(ExecuteCommandRequest.type, params);
}
}));

context.subscriptions.push(commands.registerCommand(Commands.OPEN_SETTINGS, async (settingId?: string) => {
commands.executeCommand('workbench.action.openSettings', settingId);
}));

// Setup autoCloseTags
const tagProvider = (document: TextDocument, position: Position) => {
let param = languageClient.code2ProtocolConverter.asTextDocumentPositionParams(document, position);
let text = languageClient.sendRequest(TagCloseRequest.type, param);
return text;
};
context.subscriptions.push(activateTagClosing(tagProvider, { xml: true, xsl: true }, Commands.AUTO_CLOSE_TAGS));

if (extensions.onDidChange) {// Theia doesn't support this API yet
context.subscriptions.push(extensions.onDidChange(() => {
onExtensionChange(extensions.all, getXMLConfiguration().get("extension.jars", []));
}));
}
return result;
});

const api: XMLExtensionApi = {
// add API set catalogs to internal memory
addXMLCatalogs: (catalogs: string[]) => {
const externalXmlCatalogs = externalXmlSettings.xmlCatalogs;
catalogs.forEach(element => {
if (!externalXmlCatalogs.includes(element)) {
externalXmlCatalogs.push(element);
// Copied from:
// https://github.com/redhat-developer/vscode-java/pull/1081/files
languageClient.onRequest(ConfigurationRequest.type, (params: ConfigurationParams) => {
const result: any[] = [];
const activeEditor: TextEditor | undefined = window.activeTextEditor;
for (const item of params.items) {
if (activeEditor && activeEditor.document.uri.toString() === Uri.parse(item.scopeUri).toString()) {
if (item.section === "xml.format.insertSpaces") {
result.push(activeEditor.options.insertSpaces);
} else if (item.section === "xml.format.tabSize") {
result.push(activeEditor.options.tabSize);
}
} else {
result.push(workspace.getConfiguration(null, Uri.parse(item.scopeUri)).get(item.section));
}
});
languageClient.sendNotification(DidChangeConfigurationNotification.type, { settings: getXMLSettings(requirements.java_home) });
onConfigurationChange();
},
// remove API set catalogs to internal memory
removeXMLCatalogs: (catalogs: string[]) => {
catalogs.forEach(element => {
}
return result;
});

const api: XMLExtensionApi = {
// add API set catalogs to internal memory
addXMLCatalogs: (catalogs: string[]) => {
const externalXmlCatalogs = externalXmlSettings.xmlCatalogs;
if (externalXmlCatalogs.includes(element)) {
const itemIndex = externalXmlCatalogs.indexOf(element);
externalXmlCatalogs.splice(itemIndex, 1);
}
});
languageClient.sendNotification(DidChangeConfigurationNotification.type, { settings: getXMLSettings(requirements.java_home) });
onConfigurationChange();
},
// add API set fileAssociations to internal memory
addXMLFileAssociations: (fileAssociations: XMLFileAssociation[]) => {
const externalfileAssociations = externalXmlSettings.xmlFileAssociations;
fileAssociations.forEach(element => {
if (!externalfileAssociations.some(fileAssociation => fileAssociation.systemId === element.systemId)) {
externalfileAssociations.push(element);
}
});
languageClient.sendNotification(DidChangeConfigurationNotification.type, { settings: getXMLSettings(requirements.java_home) });
onConfigurationChange();
},
// remove API set fileAssociations to internal memory
removeXMLFileAssociations: (fileAssociations: XMLFileAssociation[]) => {
const externalfileAssociations = externalXmlSettings.xmlFileAssociations;
fileAssociations.forEach(element => {
const itemIndex = externalfileAssociations.findIndex(fileAssociation => fileAssociation.systemId === element.systemId) //returns -1 if item not found
if (itemIndex > -1) {
externalfileAssociations.splice(itemIndex, 1);
}
});
languageClient.sendNotification(DidChangeConfigurationNotification.type, { settings: getXMLSettings(requirements.java_home) });
onConfigurationChange();
}
};
return api;
catalogs.forEach(element => {
if (!externalXmlCatalogs.includes(element)) {
externalXmlCatalogs.push(element);
}
});
languageClient.sendNotification(DidChangeConfigurationNotification.type, { settings: getXMLSettings(requirements.java_home) });
onConfigurationChange();
},
// remove API set catalogs to internal memory
removeXMLCatalogs: (catalogs: string[]) => {
catalogs.forEach(element => {
const externalXmlCatalogs = externalXmlSettings.xmlCatalogs;
if (externalXmlCatalogs.includes(element)) {
const itemIndex = externalXmlCatalogs.indexOf(element);
externalXmlCatalogs.splice(itemIndex, 1);
}
});
languageClient.sendNotification(DidChangeConfigurationNotification.type, { settings: getXMLSettings(requirements.java_home) });
onConfigurationChange();
},
// add API set fileAssociations to internal memory
addXMLFileAssociations: (fileAssociations: XMLFileAssociation[]) => {
const externalfileAssociations = externalXmlSettings.xmlFileAssociations;
fileAssociations.forEach(element => {
if (!externalfileAssociations.some(fileAssociation => fileAssociation.systemId === element.systemId)) {
externalfileAssociations.push(element);
}
});
languageClient.sendNotification(DidChangeConfigurationNotification.type, { settings: getXMLSettings(requirements.java_home) });
onConfigurationChange();
},
// remove API set fileAssociations to internal memory
removeXMLFileAssociations: (fileAssociations: XMLFileAssociation[]) => {
const externalfileAssociations = externalXmlSettings.xmlFileAssociations;
fileAssociations.forEach(element => {
const itemIndex = externalfileAssociations.findIndex(fileAssociation => fileAssociation.systemId === element.systemId) //returns -1 if item not found
if (itemIndex > -1) {
externalfileAssociations.splice(itemIndex, 1);
}
});
languageClient.sendNotification(DidChangeConfigurationNotification.type, { settings: getXMLSettings(requirements.java_home) });
onConfigurationChange();
}
};
return api;
});
});
});

Expand Down
Loading

0 comments on commit 8f8af65

Please sign in to comment.