From 769daea8794d2be0b40b1937910ae204365cd353 Mon Sep 17 00:00:00 2001 From: azerr Date: Thu, 3 Jun 2021 10:24:22 +0200 Subject: [PATCH] Bind XSD, DTD with CodeLens See https://github.com/redhat-developer/vscode-xml/issues/395 Signed-off-by: azerr --- .../eclipse/lemminx/XMLWorkspaceService.java | 2 +- .../contentmodel/ContentModelPlugin.java | 11 ++ .../commands/AssociateGrammarCommand.java | 143 ++++++++++++++++++ .../XMLValidationAllFilesCommand.java | 4 +- .../commands/XMLValidationFileCommand.java | 5 +- .../ContentModelCodeLensParticipant.java | 74 +++++++++ .../participants/DTDErrorCode.java | 1 - .../NoGrammarConstraintsCodeAction.java | 133 +++++++++++----- .../extensions/xsd/utils/XSDUtils.java | 11 +- .../AbstractDOMDocumentCommandHandler.java | 17 ++- .../commands/IXMLCommandService.java | 4 +- .../lemminx/MockXMLLanguageServer.java | 10 ++ .../java/org/eclipse/lemminx/XMLAssert.java | 25 +-- .../commands/AssociateGrammarCommandTest.java | 124 +++++++++++++++ .../commands/XMLValidationCommandTest.java | 16 +- .../commands/CommandServiceTest.java | 2 +- 16 files changed, 503 insertions(+), 79 deletions(-) create mode 100644 org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/commands/AssociateGrammarCommand.java create mode 100644 org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/ContentModelCodeLensParticipant.java create mode 100644 org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/commands/AssociateGrammarCommandTest.java diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/XMLWorkspaceService.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/XMLWorkspaceService.java index b9651d72a3..38720a3f0d 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/XMLWorkspaceService.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/XMLWorkspaceService.java @@ -63,7 +63,7 @@ public CompletableFuture executeCommand(ExecuteCommandParams params) { } return CompletableFutures.computeAsync(cancelChecker -> { try { - return handler.executeCommand(params, cancelChecker); + return handler.executeCommand(params, xmlLanguageServer.getSharedSettings(), cancelChecker); } catch (Exception e) { if (e instanceof ResponseErrorException) { throw (ResponseErrorException) e; diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/ContentModelPlugin.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/ContentModelPlugin.java index 40f6f1f913..6f95811ab8 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/ContentModelPlugin.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/ContentModelPlugin.java @@ -15,10 +15,12 @@ import java.util.Objects; import org.eclipse.lemminx.dom.DOMDocument; +import org.eclipse.lemminx.extensions.contentmodel.commands.AssociateGrammarCommand; import org.eclipse.lemminx.extensions.contentmodel.commands.XMLValidationAllFilesCommand; import org.eclipse.lemminx.extensions.contentmodel.commands.XMLValidationFileCommand; import org.eclipse.lemminx.extensions.contentmodel.model.ContentModelManager; import org.eclipse.lemminx.extensions.contentmodel.participants.ContentModelCodeActionParticipant; +import org.eclipse.lemminx.extensions.contentmodel.participants.ContentModelCodeLensParticipant; import org.eclipse.lemminx.extensions.contentmodel.participants.ContentModelCompletionParticipant; import org.eclipse.lemminx.extensions.contentmodel.participants.ContentModelDocumentLinkParticipant; import org.eclipse.lemminx.extensions.contentmodel.participants.ContentModelHoverParticipant; @@ -36,6 +38,7 @@ import org.eclipse.lemminx.services.extensions.ITypeDefinitionParticipant; import org.eclipse.lemminx.services.extensions.IXMLExtension; import org.eclipse.lemminx.services.extensions.XMLExtensionsRegistry; +import org.eclipse.lemminx.services.extensions.codelens.ICodeLensParticipant; import org.eclipse.lemminx.services.extensions.commands.IXMLCommandService; import org.eclipse.lemminx.services.extensions.diagnostics.IDiagnosticsParticipant; import org.eclipse.lemminx.services.extensions.save.ISaveContext; @@ -68,6 +71,8 @@ public class ContentModelPlugin implements IXMLExtension { private ContentModelSymbolsProviderParticipant symbolsProviderParticipant; + private final ICodeLensParticipant codeLensParticipant; + ContentModelManager contentModelManager; private ContentModelSettings cmSettings; @@ -80,6 +85,7 @@ public ContentModelPlugin() { diagnosticsParticipant = new ContentModelDiagnosticsParticipant(this); codeActionParticipant = new ContentModelCodeActionParticipant(); typeDefinitionParticipant = new ContentModelTypeDefinitionParticipant(); + codeLensParticipant = new ContentModelCodeLensParticipant(); } @Override @@ -176,6 +182,7 @@ public void start(InitializeParams params, XMLExtensionsRegistry registry) { registry.registerTypeDefinitionParticipant(typeDefinitionParticipant); symbolsProviderParticipant = new ContentModelSymbolsProviderParticipant(contentModelManager); registry.registerSymbolsProviderParticipant(symbolsProviderParticipant); + registry.registerCodeLensParticipant(codeLensParticipant); // Register custom commands to re-validate XML files IXMLCommandService commandService = registry.getCommandService(); @@ -186,6 +193,8 @@ public void start(InitializeParams params, XMLExtensionsRegistry registry) { new XMLValidationFileCommand(contentModelManager, documentProvider, validationService)); commandService.registerCommand(XMLValidationAllFilesCommand.COMMAND_ID, new XMLValidationAllFilesCommand(contentModelManager, documentProvider, validationService)); + commandService.registerCommand(AssociateGrammarCommand.COMMAND_ID, + new AssociateGrammarCommand(documentProvider)); } } @@ -198,12 +207,14 @@ public void stop(XMLExtensionsRegistry registry) { registry.unregisterDocumentLinkParticipant(documentLinkParticipant); registry.unregisterTypeDefinitionParticipant(typeDefinitionParticipant); registry.unregisterSymbolsProviderParticipant(symbolsProviderParticipant); + registry.unregisterCodeLensParticipant(codeLensParticipant); // Un-register custom commands to re-validate XML files IXMLCommandService commandService = registry.getCommandService(); if (commandService != null) { commandService.unregisterCommand(XMLValidationFileCommand.COMMAND_ID); commandService.unregisterCommand(XMLValidationAllFilesCommand.COMMAND_ID); + commandService.unregisterCommand(AssociateGrammarCommand.COMMAND_ID); } } diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/commands/AssociateGrammarCommand.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/commands/AssociateGrammarCommand.java new file mode 100644 index 0000000000..28d5ae622b --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/commands/AssociateGrammarCommand.java @@ -0,0 +1,143 @@ +/******************************************************************************* +* Copyright (c) 2021 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lemminx.extensions.contentmodel.commands; + +import static org.eclipse.lemminx.extensions.xsd.utils.XSDUtils.TARGET_NAMESPACE_ATTR; + +import java.net.URL; +import java.nio.file.Path; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.eclipse.lemminx.dom.DOMDocument; +import org.eclipse.lemminx.extensions.contentmodel.participants.codeactions.NoGrammarConstraintsCodeAction; +import org.eclipse.lemminx.services.IXMLDocumentProvider; +import org.eclipse.lemminx.services.extensions.commands.AbstractDOMDocumentCommandHandler; +import org.eclipse.lemminx.services.extensions.commands.ArgumentsUtils; +import org.eclipse.lemminx.settings.SharedSettings; +import org.eclipse.lemminx.utils.FilesUtils; +import org.eclipse.lemminx.utils.StringUtils; +import org.eclipse.lsp4j.ExecuteCommandParams; +import org.eclipse.lsp4j.jsonrpc.CancelChecker; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +/** + * Associate a grammar to a given XML command. + * + * @author Angelo ZERR + * + */ +public class AssociateGrammarCommand extends AbstractDOMDocumentCommandHandler { + + public static final String COMMAND_ID = "xml.associate.grammar.insert"; + + public AssociateGrammarCommand(IXMLDocumentProvider documentProvider) { + super(documentProvider); + } + + public enum GrammarBindingType { + + XSD("xsd"), // + DTD("dtd"), // + XML_MODEL("xml-model"); + + private String name; + + private GrammarBindingType(String name) { + this.name = name != null ? name : name(); + } + + public String getName() { + return name; + } + } + + @Override + protected Object executeCommand(DOMDocument document, ExecuteCommandParams params, SharedSettings sharedSettings, + CancelChecker cancelChecker) throws Exception { + String documentURI = document.getDocumentURI(); + String fullPathGrammarURI = ArgumentsUtils.getArgAt(params, 1, String.class); + String bindingType = ArgumentsUtils.getArgAt(params, 2, String.class); + String grammarURI = getRelativeURI(fullPathGrammarURI, documentURI); + + if (GrammarBindingType.XSD.getName().equals(bindingType)) { + // Check if XSD to bind declares a target namespace + String targetNamespace = getTargetNamespace(fullPathGrammarURI); + if (StringUtils.isEmpty(targetNamespace)) { + // Insert inside -> + // xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance" + // xsi:noNamespaceSchemaLocation=\"xsd/tag.xsd\" + return NoGrammarConstraintsCodeAction.createXSINoNamespaceSchemaLocationEdit(grammarURI, document); + } + // Insert inside -> + // xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + // xsi:noNamespaceSchemaLocation="xsd/tag.xsd" + return NoGrammarConstraintsCodeAction.createXSISchemaLocationEdit(grammarURI, targetNamespace, document); + } else if (GrammarBindingType.DTD.getName().equals(bindingType)) { + // Insert before -> + return NoGrammarConstraintsCodeAction.createDocTypeEdit(grammarURI, document, sharedSettings); + } else if (GrammarBindingType.XML_MODEL.getName().equals(bindingType)) { + // Insert before -> + return NoGrammarConstraintsCodeAction.createXmlModelEdit(grammarURI, document, sharedSettings); + } + return null; + } + + private static String getRelativeURI(String fullPathGrammarURI, String documentURI) { + try { + Path grammarPath = FilesUtils.getPath(fullPathGrammarURI); + Path documentPath = FilesUtils.getPath(documentURI); + Path relativePath = documentPath.getParent().relativize(grammarPath); + return relativePath.toString().replaceAll("[\\\\]", "/"); + } catch (Exception e) { + return fullPathGrammarURI; + } + } + + private static String getTargetNamespace(String xsdURI) { + TargetNamespaceHandler handler = new TargetNamespaceHandler(); + try { + SAXParserFactory factory = SAXParserFactory.newInstance(); + SAXParser saxParser = factory.newSAXParser(); + saxParser.parse(new URL(xsdURI).openStream(), handler); + } catch (Exception e) { + + } + return handler.getTargetNamespace(); + } + + /** + * SAX handler which extract the targetNamespace attribute from the xs:schema + * root tag element and null otherwise. + * + */ + private static class TargetNamespaceHandler extends DefaultHandler { + + private String targetNamespace; + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) + throws SAXException { + super.startElement(uri, localName, qName, attributes); + this.targetNamespace = attributes.getValue(TARGET_NAMESPACE_ATTR); + throw new SAXException(); + } + + public String getTargetNamespace() { + return targetNamespace; + } + } + +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/commands/XMLValidationAllFilesCommand.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/commands/XMLValidationAllFilesCommand.java index ef05dd92a1..0a8d39900a 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/commands/XMLValidationAllFilesCommand.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/commands/XMLValidationAllFilesCommand.java @@ -18,6 +18,7 @@ import org.eclipse.lemminx.services.IXMLDocumentProvider; import org.eclipse.lemminx.services.IXMLValidationService; import org.eclipse.lemminx.services.extensions.commands.IXMLCommandService.IDelegateCommandHandler; +import org.eclipse.lemminx.settings.SharedSettings; import org.eclipse.lsp4j.ExecuteCommandParams; import org.eclipse.lsp4j.jsonrpc.CancelChecker; @@ -52,7 +53,8 @@ public XMLValidationAllFilesCommand(ContentModelManager contentModelManager, IXM } @Override - public Object executeCommand(ExecuteCommandParams params, CancelChecker cancelChecker) throws Exception { + public Object executeCommand(ExecuteCommandParams params, SharedSettings sharedSettings, + CancelChecker cancelChecker) throws Exception { // 1. clear the Xerces grammar pool // (used by the Xerces validation) and the content model documents cache (used // by the XML completion/hover based on the grammar) diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/commands/XMLValidationFileCommand.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/commands/XMLValidationFileCommand.java index b6d4d56698..06c07d2cc2 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/commands/XMLValidationFileCommand.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/commands/XMLValidationFileCommand.java @@ -16,6 +16,7 @@ import org.eclipse.lemminx.services.IXMLDocumentProvider; import org.eclipse.lemminx.services.IXMLValidationService; import org.eclipse.lemminx.services.extensions.commands.AbstractDOMDocumentCommandHandler; +import org.eclipse.lemminx.settings.SharedSettings; import org.eclipse.lsp4j.ExecuteCommandParams; import org.eclipse.lsp4j.jsonrpc.CancelChecker; @@ -48,8 +49,8 @@ public XMLValidationFileCommand(ContentModelManager contentModelManager, IXMLDoc } @Override - protected Object executeCommand(DOMDocument document, ExecuteCommandParams params, CancelChecker cancelChecker) - throws Exception { + protected Object executeCommand(DOMDocument document, ExecuteCommandParams params, SharedSettings sharedSettings, + CancelChecker cancelChecker) throws Exception { // 1. remove the referenced grammar in the XML file from the Xerces grammar pool // (used by the Xerces validation) and the content model documents cache (used // by the XML completion/hover based on the grammar) diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/ContentModelCodeLensParticipant.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/ContentModelCodeLensParticipant.java new file mode 100644 index 0000000000..ec0eeee61d --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/ContentModelCodeLensParticipant.java @@ -0,0 +1,74 @@ +/******************************************************************************* +* Copyright (c) 2021 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ + +package org.eclipse.lemminx.extensions.contentmodel.participants; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.lemminx.dom.DOMDocument; +import org.eclipse.lemminx.dom.DOMElement; +import org.eclipse.lemminx.extensions.contentmodel.commands.AssociateGrammarCommand; +import org.eclipse.lemminx.extensions.contentmodel.commands.AssociateGrammarCommand.GrammarBindingType; +import org.eclipse.lemminx.services.extensions.codelens.ICodeLensParticipant; +import org.eclipse.lemminx.services.extensions.codelens.ICodeLensRequest; +import org.eclipse.lemminx.utils.XMLPositionUtility; +import org.eclipse.lsp4j.CodeLens; +import org.eclipse.lsp4j.Command; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.jsonrpc.CancelChecker; + +/** + * When XML file is not associated to a grammar (XSD, DTD), this class generates + * several CodeLenses on the root of the DOM Document: + * + *
    + *
  • [Bind with XSD] : click on this Codelens open a folder dialog to select + * the XSD to bind.
  • + *
  • [Bind with DTD] : click on this Codelens open a folder dialog to select + * the DTD to bind.
  • + *
  • [Bind with xml-model] : click on this Codelens open a folder dialog to + * select the XSD, DTD to bind.
  • + *
+ * + *

+ * Once the LSP client select the DTD, XSD, it should call the + * {@link AssociateGrammarCommand} to generate the proper syntax for binding. + *

+ * + */ +public class ContentModelCodeLensParticipant implements ICodeLensParticipant { + + private static final String COMMAND_ID = "xml.associate.grammar.selectFile"; + + @Override + public void doCodeLens(ICodeLensRequest request, List lenses, CancelChecker cancelChecker) { + DOMDocument document = request.getDocument(); + DOMElement documentElement = document.getDocumentElement(); + if (documentElement == null || document.hasGrammar()) { + return; + } + String documentURI = document.getDocumentURI(); + Range range = XMLPositionUtility.selectRootStartTag(document); + + lenses.add(createAssociateLens(documentURI, "Bind with XSD", GrammarBindingType.XSD.getName(), range)); + lenses.add(createAssociateLens(documentURI, "Bind with DTD", GrammarBindingType.DTD.getName(), range)); + lenses.add( + createAssociateLens(documentURI, "Bind with xml-model", GrammarBindingType.XML_MODEL.getName(), range)); + } + + private static CodeLens createAssociateLens(String documentURI, String title, String bindingType, Range range) { + Command command = new Command(title, COMMAND_ID, Arrays.asList(documentURI, bindingType)); + return new CodeLens(range, command, null); + } + +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/DTDErrorCode.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/DTDErrorCode.java index 9ca2845b4e..64cfe221b2 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/DTDErrorCode.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/DTDErrorCode.java @@ -20,7 +20,6 @@ import org.apache.xerces.xni.XMLLocator; import org.eclipse.lemminx.commons.BadLocationException; import org.eclipse.lemminx.dom.DOMDocument; -import org.eclipse.lemminx.dom.DOMDocumentType; import org.eclipse.lemminx.dom.DOMElement; import org.eclipse.lemminx.dom.DOMRange; import org.eclipse.lemminx.extensions.contentmodel.participants.codeactions.ElementDeclUnterminatedCodeAction; diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/codeactions/NoGrammarConstraintsCodeAction.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/codeactions/NoGrammarConstraintsCodeAction.java index ce5ced722b..5a10386a86 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/codeactions/NoGrammarConstraintsCodeAction.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/codeactions/NoGrammarConstraintsCodeAction.java @@ -55,9 +55,6 @@ public void doCodeAction(Diagnostic diagnostic, Range range, DOMDocument documen } FileContentGeneratorManager generator = componentProvider.getComponent(FileContentGeneratorManager.class); - String delimiter = document.lineDelimiter(0); - int beforeTagOffset = documentElement.getStartTagOpenOffset(); - int afterTagOffset = beforeTagOffset + 1 + documentElement.getTagName().length(); // ---------- XSD @@ -67,23 +64,18 @@ public void doCodeAction(Diagnostic diagnostic, Range range, DOMDocument documen // xsi:noNamespaceSchemaLocation // Create code action to create the XSD file with the generated XSD content - String insertText = " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" - + document.getTextDocument().lineDelimiter(0); - insertText += " xsi:noNamespaceSchemaLocation=\"" + schemaFileName + "\""; + TextDocumentEdit noNamespaceSchemaLocationEdit = createXSINoNamespaceSchemaLocationEdit(schemaFileName, + document); CodeAction noNamespaceSchemaLocationAction = createGrammarFileAndBindIt( "Generate '" + schemaFileName + "' and bind with xsi:noNamespaceSchemaLocation", schemaURI, - schemaTemplate, insertText, afterTagOffset, document, diagnostic); + schemaTemplate, noNamespaceSchemaLocationEdit, diagnostic); codeActions.add(noNamespaceSchemaLocationAction); // xml-model - XMLBuilder xsdWithXmlModel = new XMLBuilder(sharedSettings, null, delimiter); - xsdWithXmlModel.startPrologOrPI("xml-model"); - xsdWithXmlModel.addSingleAttribute("href", schemaFileName, true); - xsdWithXmlModel.endPrologOrPI(); - xsdWithXmlModel.linefeed(); + TextDocumentEdit xsdWithXMLModelEdit = createXmlModelEdit(schemaFileName, document, sharedSettings); CodeAction xsdWithXmlModelAction = createGrammarFileAndBindIt( "Generate '" + schemaFileName + "' and bind with xml-model", schemaURI, schemaTemplate, - xsdWithXmlModel.toString(), beforeTagOffset, document, diagnostic); + xsdWithXMLModelEdit, diagnostic); codeActions.add(xsdWithXmlModelAction); // ---------- DTD @@ -93,28 +85,17 @@ public void doCodeAction(Diagnostic diagnostic, Range range, DOMDocument documen String dtdTemplate = generator.generate(document, sharedSettings, new DTDGeneratorSettings()); // - XMLBuilder docType = new XMLBuilder(sharedSettings, null, delimiter); - docType.startDoctype(); - docType.addParameter(documentElement.getLocalName()); - docType.addContent(" SYSTEM \""); - docType.addContent(dtdFileName); - docType.addContent("\""); - docType.endDoctype(); - docType.linefeed(); + TextDocumentEdit dtdWithDocType = createDocTypeEdit(dtdFileName, document, sharedSettings); CodeAction docTypeAction = createGrammarFileAndBindIt( - "Generate '" + dtdFileName + "' and bind with DOCTYPE", dtdURI, dtdTemplate, docType.toString(), - beforeTagOffset, document, diagnostic); + "Generate '" + dtdFileName + "' and bind with DOCTYPE", dtdURI, dtdTemplate, dtdWithDocType, + diagnostic); codeActions.add(docTypeAction); // xml-model - XMLBuilder dtdWithXmlModel = new XMLBuilder(sharedSettings, null, delimiter); - dtdWithXmlModel.startPrologOrPI("xml-model"); - dtdWithXmlModel.addSingleAttribute("href", dtdFileName, true); - dtdWithXmlModel.endPrologOrPI(); - dtdWithXmlModel.linefeed(); + TextDocumentEdit dtdWithXMLModelEdit = createXmlModelEdit(dtdFileName, document, sharedSettings); CodeAction dtdWithXmlModelAction = createGrammarFileAndBindIt( - "Generate '" + dtdFileName + "' and bind with xml-model", dtdURI, dtdTemplate, - dtdWithXmlModel.toString(), beforeTagOffset, document, diagnostic); + "Generate '" + dtdFileName + "' and bind with xml-model", dtdURI, dtdTemplate, dtdWithXMLModelEdit, + diagnostic); codeActions.add(dtdWithXmlModelAction); } catch (BadLocationException e) { @@ -153,18 +134,94 @@ static String getFileName(String schemaURI) { return new File(schemaURI).getName(); } - private static CodeAction createGrammarFileAndBindIt(String title, String grammarURI, String grammarContent, - String insertText, int insertOffset, DOMDocument document, Diagnostic diagnostic) - throws BadLocationException { - Position position = document.positionAt(insertOffset); - TextDocumentEdit insertEdit = CodeActionFactory.insertEdit(insertText, position, document.getTextDocument()); - return createGrammarFileAndBindIt(title, grammarURI, grammarContent, insertEdit, diagnostic); - } - private static CodeAction createGrammarFileAndBindIt(String title, String grammarURI, String grammarContent, TextDocumentEdit boundEdit, Diagnostic diagnostic) { CodeAction codeAction = CodeActionFactory.createFile(title, grammarURI, grammarContent, diagnostic); codeAction.getEdit().getDocumentChanges().add(Either.forLeft(boundEdit)); return codeAction; } + + public static TextDocumentEdit createXSINoNamespaceSchemaLocationEdit(String schemaFileName, DOMDocument document) + throws BadLocationException { + String delimiter = document.getTextDocument().lineDelimiter(0); + DOMElement documentElement = document.getDocumentElement(); + int beforeTagOffset = documentElement.getStartTagOpenOffset(); + int afterTagOffset = beforeTagOffset + 1 + documentElement.getTagName().length(); + + StringBuilder insertText = new StringBuilder(); + + insertText.append(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""); + insertText.append(delimiter); + + insertText.append(" xsi:noNamespaceSchemaLocation=\""); + insertText.append(schemaFileName); + insertText.append("\""); + + Position position = document.positionAt(afterTagOffset); + return CodeActionFactory.insertEdit(insertText.toString(), position, document.getTextDocument()); + } + + public static TextDocumentEdit createXSISchemaLocationEdit(String schemaFileName, String targetNamespace, + DOMDocument document) throws BadLocationException { + String delimiter = document.getTextDocument().lineDelimiter(0); + DOMElement documentElement = document.getDocumentElement(); + int beforeTagOffset = documentElement.getStartTagOpenOffset(); + int afterTagOffset = beforeTagOffset + 1 + documentElement.getTagName().length(); + + StringBuilder insertText = new StringBuilder(); + insertText.append(" xmlns=\""); + insertText.append(targetNamespace); + insertText.append("\""); + insertText.append(delimiter); + + insertText.append(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""); + insertText.append(delimiter); + + insertText.append(" xsi:schemaLocation=\""); + insertText.append(targetNamespace); + insertText.append(" "); + insertText.append(schemaFileName); + insertText.append("\""); + + Position position = document.positionAt(afterTagOffset); + return CodeActionFactory.insertEdit(insertText.toString(), position, document.getTextDocument()); + } + + public static TextDocumentEdit createXmlModelEdit(String schemaFileName, DOMDocument document, + SharedSettings sharedSettings) throws BadLocationException { + String delimiter = document.getTextDocument().lineDelimiter(0); + DOMElement documentElement = document.getDocumentElement(); + int beforeTagOffset = documentElement.getStartTagOpenOffset(); + + XMLBuilder xsdWithXmlModel = new XMLBuilder(sharedSettings, null, delimiter); + xsdWithXmlModel.startPrologOrPI("xml-model"); + xsdWithXmlModel.addSingleAttribute("href", schemaFileName, true); + xsdWithXmlModel.endPrologOrPI(); + xsdWithXmlModel.linefeed(); + + String insertText = xsdWithXmlModel.toString(); + Position position = document.positionAt(beforeTagOffset); + return CodeActionFactory.insertEdit(insertText, position, document.getTextDocument()); + } + + public static TextDocumentEdit createDocTypeEdit(String dtdFileName, DOMDocument document, + SharedSettings sharedSettings) throws BadLocationException { + String delimiter = document.getTextDocument().lineDelimiter(0); + DOMElement documentElement = document.getDocumentElement(); + int beforeTagOffset = documentElement.getStartTagOpenOffset(); + + XMLBuilder docType = new XMLBuilder(sharedSettings, null, delimiter); + docType.startDoctype(); + docType.addParameter(documentElement.getLocalName()); + docType.addContent(" SYSTEM \""); + docType.addContent(dtdFileName); + docType.addContent("\""); + docType.endDoctype(); + docType.linefeed(); + + String insertText = docType.toString(); + Position position = document.positionAt(beforeTagOffset); + return CodeActionFactory.insertEdit(insertText, position, document.getTextDocument()); + } + } diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsd/utils/XSDUtils.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsd/utils/XSDUtils.java index 911bc5b279..f7ea55e0b2 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsd/utils/XSDUtils.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsd/utils/XSDUtils.java @@ -47,6 +47,9 @@ */ public class XSDUtils { + public static final String SCHEMA_LOCATION_ATTR = "schemaLocation"; + public static final String TARGET_NAMESPACE_ATTR = "targetNamespace"; + /** * Binding type of xs attribute. * @@ -152,7 +155,7 @@ public static void searchXSTargetAttributes(DOMAttr originAttr, BindingType bind // - String targetNamespace = documentElement.getAttribute("targetNamespace"); // -> + String targetNamespace = documentElement.getAttribute(TARGET_NAMESPACE_ATTR); // -> // http://camel.apache.org/schema/spring String targetNamespacePrefix = documentElement.getPrefix(targetNamespace); // -> tns @@ -190,7 +193,7 @@ private static void searchXSTargetAttributes(DOMAttr originAttr, BindingType bin } } else if (isXSInclude(targetElement)) { // collect xs:include XML Schema location - String schemaLocation = targetElement.getAttribute("schemaLocation"); + String schemaLocation = targetElement.getAttribute(SCHEMA_LOCATION_ATTR); if (schemaLocation != null) { if (externalURIS == null) { externalURIS = new HashSet<>(); @@ -271,7 +274,7 @@ public static void searchXSOriginAttributes(DOMNode targetNode, BiConsumer - String targetNamespace = documentElement.getAttribute("targetNamespace"); // -> + String targetNamespace = documentElement.getAttribute(TARGET_NAMESPACE_ATTR); // -> // http://camel.apache.org/schema/spring String targetNamespacePrefix = documentElement.getPrefix(targetNamespace); // -> tns @@ -464,7 +467,7 @@ public static DOMAttr getSchemaLocation(DOMElement element) { if (!(isXSInclude(element) || isXSImport(element))) { return null; } - return element.getAttributeNode("schemaLocation"); + return element.getAttributeNode(SCHEMA_LOCATION_ATTR); } } diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/commands/AbstractDOMDocumentCommandHandler.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/commands/AbstractDOMDocumentCommandHandler.java index 140a47af31..a031fed6d8 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/commands/AbstractDOMDocumentCommandHandler.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/commands/AbstractDOMDocumentCommandHandler.java @@ -14,6 +14,7 @@ import org.eclipse.lemminx.dom.DOMDocument; import org.eclipse.lemminx.services.IXMLDocumentProvider; import org.eclipse.lemminx.services.extensions.commands.IXMLCommandService.IDelegateCommandHandler; +import org.eclipse.lemminx.settings.SharedSettings; import org.eclipse.lsp4j.ExecuteCommandParams; import org.eclipse.lsp4j.TextDocumentIdentifier; import org.eclipse.lsp4j.jsonrpc.CancelChecker; @@ -34,7 +35,8 @@ public AbstractDOMDocumentCommandHandler(IXMLDocumentProvider documentProvider) } @Override - public final Object executeCommand(ExecuteCommandParams params, CancelChecker cancelChecker) throws Exception { + public final Object executeCommand(ExecuteCommandParams params, SharedSettings sharedSettings, + CancelChecker cancelChecker) throws Exception { TextDocumentIdentifier identifier = ArgumentsUtils.getArgAt(params, 0, TextDocumentIdentifier.class); String uri = identifier.getUri(); DOMDocument document = documentProvider.getDocument(uri); @@ -42,23 +44,24 @@ public final Object executeCommand(ExecuteCommandParams params, CancelChecker ca throw new UnsupportedOperationException(String .format("Command '%s' cannot find the DOM document with the URI '%s'.", params.getCommand(), uri)); } - return executeCommand(document, params, cancelChecker); + return executeCommand(document, params, sharedSettings, cancelChecker); } /** * Executes a command * - * @param document the DOM document retrieve by the - * {@link TextDocumentIdentifier} argument. + * @param document the DOM document retrieve by the + * {@link TextDocumentIdentifier} argument. * - * @param params command execution parameters - * @param cancelChecker check if cancel has been requested + * @param params command execution parameters + * @param sharedSettings the shared settings + * @param cancelChecker check if cancel has been requested * @return the result of the command * @throws Exception the unhandled exception will be wrapped in * org.eclipse.lsp4j.jsonrpc.ResponseErrorException * and be wired back to the JSON-RPC protocol caller */ protected abstract Object executeCommand(DOMDocument document, ExecuteCommandParams params, - CancelChecker cancelChecker) throws Exception; + SharedSettings sharedSettings, CancelChecker cancelChecker) throws Exception; } \ No newline at end of file diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/commands/IXMLCommandService.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/commands/IXMLCommandService.java index 3940660de7..6e95461683 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/commands/IXMLCommandService.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/commands/IXMLCommandService.java @@ -14,6 +14,7 @@ import java.util.concurrent.CompletableFuture; +import org.eclipse.lemminx.settings.SharedSettings; import org.eclipse.lsp4j.ExecuteCommandParams; import org.eclipse.lsp4j.jsonrpc.CancelChecker; @@ -37,13 +38,14 @@ public interface IDelegateCommandHandler { * Executes a command * * @param params command execution parameters + * @param sharedSettings the shared settings. * @param cancelChecker check if cancel has been requested * @return the result of the command * @throws Exception the unhandled exception will be wrapped in * org.eclipse.lsp4j.jsonrpc.ResponseErrorException * and be wired back to the JSON-RPC protocol caller */ - Object executeCommand(ExecuteCommandParams params, CancelChecker cancelChecker) throws Exception; + Object executeCommand(ExecuteCommandParams params, SharedSettings sharedSettings, CancelChecker cancelChecker) throws Exception; } /** diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/MockXMLLanguageServer.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/MockXMLLanguageServer.java index 5196ad64d0..e5118c1645 100644 --- a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/MockXMLLanguageServer.java +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/MockXMLLanguageServer.java @@ -17,9 +17,12 @@ import org.eclipse.lemminx.customservice.ActionableNotification; import org.eclipse.lemminx.services.extensions.commands.IXMLCommandService.IDelegateCommandHandler; +import org.eclipse.lsp4j.DidOpenTextDocumentParams; import org.eclipse.lsp4j.ExecuteCommandParams; import org.eclipse.lsp4j.MessageParams; import org.eclipse.lsp4j.PublishDiagnosticsParams; +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.eclipse.lsp4j.TextDocumentItem; /** * Mock XML Language server which helps to track show messages, actionable @@ -65,4 +68,11 @@ public CompletableFuture executeCommand(String command, Object... argume return getWorkspaceService().executeCommand(params); } + public TextDocumentIdentifier didOpen(String fileURI, String xml) { + TextDocumentIdentifier xmlIdentifier = new TextDocumentIdentifier(fileURI); + DidOpenTextDocumentParams params = new DidOpenTextDocumentParams( + new TextDocumentItem(xmlIdentifier.getUri(), "xml", 1, xml)); + getTextDocumentService().didOpen(params); + return xmlIdentifier; + } } diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/XMLAssert.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/XMLAssert.java index 1dcdc7a932..6f38e9da95 100644 --- a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/XMLAssert.java +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/XMLAssert.java @@ -18,7 +18,6 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.nio.file.Path; @@ -94,7 +93,7 @@ import org.junit.jupiter.api.Assertions; /** - * XML + * XML Assert * */ public class XMLAssert { @@ -123,7 +122,8 @@ public class XMLAssert { private static final String FILE_URI = "test.xml"; - private static final CancelChecker NULL_CHECKER = () -> {}; + private static final CancelChecker NULL_CHECKER = () -> { + }; public static class SettingsSaveContext extends AbstractSaveContext { @@ -314,7 +314,8 @@ public static void testTagCompletion(String value, String expected) throws BadLo testTagCompletion(value, expected, new SharedSettings()); } - public static void testTagCompletion(String value, String expected, SharedSettings settings) throws BadLocationException { + public static void testTagCompletion(String value, String expected, SharedSettings settings) + throws BadLocationException { int offset = value.indexOf('|'); value = value.substring(0, offset) + value.substring(offset + 1); @@ -333,7 +334,8 @@ public static void testTagCompletion(String value, String expected, SharedSettin assertEquals(expected, actual); } - public static void testTagCompletion(String value, AutoCloseTagResponse expected, SharedSettings settings) throws BadLocationException { + public static void testTagCompletion(String value, AutoCloseTagResponse expected, SharedSettings settings) + throws BadLocationException { int offset = value.indexOf('|'); value = value.substring(0, offset) + value.substring(offset + 1); @@ -628,15 +630,17 @@ public static CodeAction ca(Diagnostic d, TextEdit... te) { codeAction.setTitle(""); codeAction.setDiagnostics(Arrays.asList(d)); - VersionedTextDocumentIdentifier versionedTextDocumentIdentifier = new VersionedTextDocumentIdentifier(FILE_URI, - 0); - - TextDocumentEdit textDocumentEdit = new TextDocumentEdit(versionedTextDocumentIdentifier, Arrays.asList(te)); + TextDocumentEdit textDocumentEdit = tde(FILE_URI, te); WorkspaceEdit workspaceEdit = new WorkspaceEdit(Collections.singletonList(Either.forLeft(textDocumentEdit))); codeAction.setEdit(workspaceEdit); return codeAction; } + public static TextDocumentEdit tde(String uri, TextEdit... te) { + VersionedTextDocumentIdentifier versionedTextDocumentIdentifier = new VersionedTextDocumentIdentifier(uri, 1); + return new TextDocumentEdit(versionedTextDocumentIdentifier, Arrays.asList(te)); + } + public static CodeAction ca(Diagnostic d, Either... ops) { CodeAction codeAction = new CodeAction(); codeAction.setDiagnostics(Collections.singletonList(d)); @@ -1325,7 +1329,8 @@ public static void testSelectionRange(String xml, SelectionRange... selectionRan stringBuilder.deleteCharAt(nextPipe); nextPipe = stringBuilder.indexOf("|"); } - assertEquals(selectionRanges.length, cursorOffsets.size(), "Number of cursors and SelectionRanges should be equal"); + assertEquals(selectionRanges.length, cursorOffsets.size(), + "Number of cursors and SelectionRanges should be equal"); testSelectionRange(stringBuilder.toString(), cursorOffsets, selectionRanges); } diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/commands/AssociateGrammarCommandTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/commands/AssociateGrammarCommandTest.java new file mode 100644 index 0000000000..511c98ce34 --- /dev/null +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/commands/AssociateGrammarCommandTest.java @@ -0,0 +1,124 @@ +/******************************************************************************* +* Copyright (c) 2021 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lemminx.extensions.contentmodel.commands; + +import static org.eclipse.lemminx.XMLAssert.tde; +import static org.eclipse.lemminx.XMLAssert.te; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.io.File; +import java.util.concurrent.ExecutionException; + +import org.eclipse.lemminx.MockXMLLanguageServer; +import org.eclipse.lemminx.extensions.contentmodel.commands.AssociateGrammarCommand.GrammarBindingType; +import org.eclipse.lemminx.utils.platform.Platform; +import org.eclipse.lsp4j.TextDocumentEdit; +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.junit.jupiter.api.Test; + +/** + * Test for associating a grammar to a given XML command. + * + * @author Angelo ZERR + * + */ +public class AssociateGrammarCommandTest { + + @Test + public void associateWithXSDNoNamespaceShemaLocation() throws InterruptedException, ExecutionException { + MockXMLLanguageServer languageServer = new MockXMLLanguageServer(); + + String xml = "\r\n" + // + ""; + String xmlPath = getFileURI("src/test/resources/tag.xml"); + TextDocumentIdentifier xmlIdentifier = languageServer.didOpen(xmlPath, xml); + String xsdPath = getFileURI("src/test/resources/xsd/tag.xsd"); + String bindingType = GrammarBindingType.XSD.getName(); + + TextDocumentEdit actual = (TextDocumentEdit) languageServer + .executeCommand(AssociateGrammarCommand.COMMAND_ID, xmlIdentifier, xsdPath, bindingType).get(); + assertNotNull(actual); + + assertEquals(actual, tde(xmlPath, te(1, 4, 1, 4, // + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n" + // + " xsi:noNamespaceSchemaLocation=\"xsd/tag.xsd\""))); + } + + @Test + public void associateWithXSDSchemaLocation() throws Exception { + MockXMLLanguageServer languageServer = new MockXMLLanguageServer(); + + String xml = "\r\n" + // + ""; + String xmlPath = getFileURI("src/test/resources/tag.xml"); + TextDocumentIdentifier xmlIdentifier = languageServer.didOpen(xmlPath, xml); + + String xsdPath = getFileURI("src/test/resources/xsd/team.xsd"); + String bindingType = GrammarBindingType.XSD.getName(); + + TextDocumentEdit actual = (TextDocumentEdit) languageServer + .executeCommand(AssociateGrammarCommand.COMMAND_ID, xmlIdentifier, xsdPath, bindingType).get(); + assertNotNull(actual); + + assertEquals(actual, tde(xmlPath, te(1, 4, 1, 4, // + " xmlns=\"team_namespace\"\r\n" + // + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n" + // + " xsi:schemaLocation=\"team_namespace xsd/team.xsd\""))); + } + + @Test + public void associateWithDTD() throws InterruptedException, ExecutionException { + MockXMLLanguageServer languageServer = new MockXMLLanguageServer(); + + String xml = "\r\n" + // + ""; + String xmlPath = getFileURI("src/test/resources/tag.xml"); + TextDocumentIdentifier xmlIdentifier = languageServer.didOpen(xmlPath, xml); + String dtdPath = getFileURI("src/test/resources/dtd/tag.dtd"); + String bindingType = GrammarBindingType.DTD.getName(); + + TextDocumentEdit actual = (TextDocumentEdit) languageServer + .executeCommand(AssociateGrammarCommand.COMMAND_ID, xmlIdentifier, dtdPath, bindingType).get(); + assertNotNull(actual); + + assertEquals(actual, tde(xmlPath, te(1, 0, 1, 0, // + "\r\n"))); + } + + @Test + public void associateWithXMLModel() throws InterruptedException, ExecutionException { + MockXMLLanguageServer languageServer = new MockXMLLanguageServer(); + + String xml = "\r\n" + // + ""; + String xmlPath = getFileURI("src/test/resources/tag.xml"); + TextDocumentIdentifier xmlIdentifier = languageServer.didOpen(xmlPath, xml); + String dtdPath = getFileURI("src/test/resources/dtd/tag.dtd"); + String bindingType = GrammarBindingType.XML_MODEL.getName(); + + TextDocumentEdit actual = (TextDocumentEdit) languageServer + .executeCommand(AssociateGrammarCommand.COMMAND_ID, xmlIdentifier, dtdPath, bindingType).get(); + assertNotNull(actual); + + assertEquals(actual, tde(xmlPath, te(1, 0, 1, 0, // + "\r\n"))); + } + + private static String getFileURI(String fileName) { + String uri = new File(fileName).toURI().toString(); + if (Platform.isWindows && !uri.startsWith("file://")) { + uri = uri.replace("file:/", "file:///"); + } + return uri; + } +} diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/commands/XMLValidationCommandTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/commands/XMLValidationCommandTest.java index db87a4953f..3bf8cd6aaf 100644 --- a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/commands/XMLValidationCommandTest.java +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/commands/XMLValidationCommandTest.java @@ -32,11 +32,9 @@ import org.eclipse.lsp4j.CompletionList; import org.eclipse.lsp4j.CompletionParams; import org.eclipse.lsp4j.DiagnosticSeverity; -import org.eclipse.lsp4j.DidOpenTextDocumentParams; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.PublishDiagnosticsParams; import org.eclipse.lsp4j.TextDocumentIdentifier; -import org.eclipse.lsp4j.TextDocumentItem; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.junit.jupiter.api.Test; @@ -83,7 +81,7 @@ public void validationFileCommand() throws Exception { ""; // Open the XML document, the validation is triggered asynchronously - TextDocumentIdentifier xmlIdentifier = didOpen(languageServer, "test.xml", xml); + TextDocumentIdentifier xmlIdentifier = languageServer.didOpen("test.xml", xml); // Wait for: // - downloading of XSD from the HTTP server to lemminx cache @@ -209,7 +207,7 @@ public void validationAllFilesCommand() throws Exception { ""; // Open the XML document, the validation is triggered asynchronously - TextDocumentIdentifier xml1Identifier = didOpen(languageServer, "test1.xml", xml1); + TextDocumentIdentifier xml1Identifier = languageServer.didOpen("test1.xml", xml1); // Wait for to collect diagnostics in the proper order (XSD diagnostics followed // by DTD diagnostics) @@ -223,7 +221,7 @@ public void validationAllFilesCommand() throws Exception { ""; // Open the XML document, the validation is triggered asynchronously - TextDocumentIdentifier xml2Identifier = didOpen(languageServer, "test2.xml", xml2); + TextDocumentIdentifier xml2Identifier = languageServer.didOpen("test2.xml", xml2); // Wait for: // - downloading of XSD from the HTTP server to lemminx cache @@ -331,14 +329,6 @@ public void validationAllFilesCommand() throws Exception { } } - private static TextDocumentIdentifier didOpen(MockXMLLanguageServer languageServer, String fileURI, String xml) { - TextDocumentIdentifier xmlIdentifier = new TextDocumentIdentifier(fileURI); - DidOpenTextDocumentParams params = new DidOpenTextDocumentParams( - new TextDocumentItem(xmlIdentifier.getUri(), "xml", 1, xml)); - languageServer.getTextDocumentService().didOpen(params); - return xmlIdentifier; - } - private static CompletionList completion(MockXMLLanguageServer languageServer, TextDocumentIdentifier xmlIdentifier) throws BadLocationException, InterruptedException, ExecutionException { DOMDocument document = languageServer.getDocument(xmlIdentifier.getUri()); diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/extensions/commands/CommandServiceTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/extensions/commands/CommandServiceTest.java index 14fe5d13bb..fa9dea9997 100644 --- a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/extensions/commands/CommandServiceTest.java +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/extensions/commands/CommandServiceTest.java @@ -36,7 +36,7 @@ public CommandServiceTest() { } private void registerCommand(String command) { - languageServer.registerCommand(command, (params, cancelChecker) -> { + languageServer.registerCommand(command, (params, sharedSettings, cancelChecker) -> { return params.getCommand() + (params.getArguments().isEmpty() ? "" : ": " + params.getArguments().stream().map(a -> a.toString()).collect(Collectors.joining(" "))); });