From a7bafefa0fdeea44ee35d860e3cade1461d4d926 Mon Sep 17 00:00:00 2001 From: azerr Date: Thu, 23 Nov 2023 08:40:07 +0100 Subject: [PATCH] XML Files support with settings Fixes #1464 Signed-off-by: azerr --- .../extensions/catalog/XMLCatalogPlugin.java | 6 +- .../CatalogFilePathSupportParticipant.java | 45 +++ .../XMLCatalogCodeLensParticipant.java | 2 +- .../XMLCatalogDiagnosticsParticipant.java | 7 +- .../XMLCatalogDocumentLinkParticipant.java | 4 +- .../extensions/filepath/FilePathPlugin.java | 152 ++++++++++ .../filepath/IFilePathExpression.java | 56 ++++ .../filepath/IFilePathSupportParticipant.java | 39 +++ .../filepath/SimpleFilePathExpression.java | 46 +++ .../FilePathCompletionParticipant.java | 259 ++++++++++++++++ .../FilePathCompletionResult.java | 177 +++++++++++ .../filepath/settings/FilePathExpression.java | 106 +++++++ .../filepath/settings/FilePathMapping.java | 46 +++ .../filepath/settings/FilePathSupport.java | 30 ++ .../settings/FilePathSupportSettings.java | 66 ++++ .../extensions/general/FilePathPlugin.java | 40 --- .../FilePathCompletionParticipant.java | 286 ------------------ .../rng/RNGFilePathSupportParticipant.java | 49 +++ .../XSDDocumentLinkParticipant.java | 4 +- .../XSDFilePathSupportParticipant.java | 64 ++++ .../XSLFilePathSupportParticipant.java | 65 ++++ .../snippets/NewFileSnippetContext.java | 10 +- .../snippets/XMLSnippetRegistryLoader.java | 5 +- .../lemminx/settings/PathPatternMatcher.java | 3 +- .../utils/CompletionItemDefaultsUtils.java | 14 +- .../org/eclipse/lemminx/utils/DOMUtils.java | 34 +++ .../org/eclipse/lemminx/utils/FilesUtils.java | 24 +- .../eclipse/lemminx/utils/StringUtils.java | 2 +- .../META-INF/native-image/reflect-config.json | 32 ++ ...sions.filepath.IFilePathSupportParticipant | 4 + ....lemminx.services.extensions.IXMLExtension | 2 +- .../services/snippets/new-xsl-snippets.json | 16 + .../FilePathCompletionWithCatalogTest.java | 40 +++ .../filepath/FilePathSettingsUtils.java | 64 ++++ .../AbstractFilePathCompletionTest.java | 76 +++++ .../FilePathCompletionWithDOCTYPETest.java | 103 +++++++ ...tionWithNoNamespaceSchemaLocationTest.java | 122 ++++++++ ...ePathCompletionWithSchemaLocationTest.java | 38 +++ .../FilePathCompletionWithSettingsTest.java} | 246 +++++++-------- .../rng/FilePathCompletionWithRNGTest.java | 53 ++++ .../xsd/FilePathCompletionWithXSDTest.java | 51 ++++ .../xsl/FilePathCompletionWithXSLTest.java | 51 ++++ .../filePathCompletion/folderA/dtdA1.dtd | 0 .../filePathCompletion/folderA/dtdA2.dtd | 0 .../filePathCompletion/folderB/dtdB1.dtd | 0 .../resources/filePathCompletion/main.dtd | 0 .../resources/filePathCompletion/main.xsd | 0 .../resources/filePathCompletion/main.xsl | 0 48 files changed, 2053 insertions(+), 486 deletions(-) create mode 100644 org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/participants/CatalogFilePathSupportParticipant.java rename org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/{ => participants}/XMLCatalogCodeLensParticipant.java (98%) rename org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/{ => participants}/XMLCatalogDiagnosticsParticipant.java (86%) rename org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/{ => participants}/XMLCatalogDocumentLinkParticipant.java (92%) create mode 100644 org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/FilePathPlugin.java create mode 100644 org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/IFilePathExpression.java create mode 100644 org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/IFilePathSupportParticipant.java create mode 100644 org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/SimpleFilePathExpression.java create mode 100644 org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionParticipant.java create mode 100644 org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionResult.java create mode 100644 org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/settings/FilePathExpression.java create mode 100644 org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/settings/FilePathMapping.java create mode 100644 org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/settings/FilePathSupport.java create mode 100644 org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/settings/FilePathSupportSettings.java delete mode 100644 org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/general/FilePathPlugin.java delete mode 100644 org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/general/completion/FilePathCompletionParticipant.java create mode 100644 org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/relaxng/grammar/rng/RNGFilePathSupportParticipant.java create mode 100644 org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsd/participants/XSDFilePathSupportParticipant.java create mode 100644 org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsl/participants/XSLFilePathSupportParticipant.java create mode 100644 org.eclipse.lemminx/src/main/resources/META-INF/services/org.eclipse.lemminx.extensions.filepath.IFilePathSupportParticipant create mode 100644 org.eclipse.lemminx/src/main/resources/org/eclipse/lemminx/services/snippets/new-xsl-snippets.json create mode 100644 org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/catalog/FilePathCompletionWithCatalogTest.java create mode 100644 org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/filepath/FilePathSettingsUtils.java create mode 100644 org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/filepath/participants/AbstractFilePathCompletionTest.java create mode 100644 org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionWithDOCTYPETest.java create mode 100644 org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionWithNoNamespaceSchemaLocationTest.java create mode 100644 org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionWithSchemaLocationTest.java rename org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/{general/FilePathCompletionTest.java => filepath/participants/FilePathCompletionWithSettingsTest.java} (52%) create mode 100644 org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/relaxng/grammar/rng/FilePathCompletionWithRNGTest.java create mode 100644 org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/xsd/FilePathCompletionWithXSDTest.java create mode 100644 org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/xsl/FilePathCompletionWithXSLTest.java create mode 100644 org.eclipse.lemminx/src/test/resources/filePathCompletion/folderA/dtdA1.dtd create mode 100644 org.eclipse.lemminx/src/test/resources/filePathCompletion/folderA/dtdA2.dtd create mode 100644 org.eclipse.lemminx/src/test/resources/filePathCompletion/folderB/dtdB1.dtd create mode 100644 org.eclipse.lemminx/src/test/resources/filePathCompletion/main.dtd create mode 100644 org.eclipse.lemminx/src/test/resources/filePathCompletion/main.xsd create mode 100644 org.eclipse.lemminx/src/test/resources/filePathCompletion/main.xsl diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/XMLCatalogPlugin.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/XMLCatalogPlugin.java index 00f751d78..6439ee642 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/XMLCatalogPlugin.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/XMLCatalogPlugin.java @@ -19,6 +19,9 @@ import org.eclipse.lemminx.client.InvalidPathWarner; import org.eclipse.lemminx.client.PathFeature; +import org.eclipse.lemminx.extensions.catalog.participants.XMLCatalogCodeLensParticipant; +import org.eclipse.lemminx.extensions.catalog.participants.XMLCatalogDiagnosticsParticipant; +import org.eclipse.lemminx.extensions.catalog.participants.XMLCatalogDocumentLinkParticipant; import org.eclipse.lemminx.extensions.contentmodel.model.ContentModelManager; import org.eclipse.lemminx.extensions.contentmodel.settings.ContentModelSettings; import org.eclipse.lemminx.services.IXMLNotificationService; @@ -51,7 +54,8 @@ public XMLCatalogPlugin() { @Override public void doSave(ISaveContext context) { Object initializationOptionsSettings = context.getSettings(); - ContentModelSettings cmSettings = ContentModelSettings.getContentModelXMLSettings(initializationOptionsSettings); + ContentModelSettings cmSettings = ContentModelSettings + .getContentModelXMLSettings(initializationOptionsSettings); if (cmSettings == null) { return; } diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/participants/CatalogFilePathSupportParticipant.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/participants/CatalogFilePathSupportParticipant.java new file mode 100644 index 000000000..e30f94270 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/participants/CatalogFilePathSupportParticipant.java @@ -0,0 +1,45 @@ +/******************************************************************************* +* Copyright (c) 2023 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.catalog.participants; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.eclipse.lemminx.dom.DOMDocument; +import org.eclipse.lemminx.extensions.filepath.IFilePathExpression; +import org.eclipse.lemminx.extensions.filepath.IFilePathSupportParticipant; +import org.eclipse.lemminx.extensions.filepath.settings.FilePathExpression; +import org.eclipse.lemminx.utils.DOMUtils; + +/** + * Catalog file path support for catalog.xml to provide completion for @uri + * attribute. + */ +public class CatalogFilePathSupportParticipant implements IFilePathSupportParticipant { + + private static final List CATALOG_FILE_PATH_EXPRESSIONS; + + static { + CATALOG_FILE_PATH_EXPRESSIONS = Arrays.asList(new FilePathExpression("@uri")); + } + + @Override + public List collectFilePathExpressions(DOMDocument document) { + if (!DOMUtils.isCatalog(document)) { + return Collections.emptyList(); + } + // The DOM document is an XML catalog, returns their expressions. + return CATALOG_FILE_PATH_EXPRESSIONS; + } + +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/XMLCatalogCodeLensParticipant.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/participants/XMLCatalogCodeLensParticipant.java similarity index 98% rename from org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/XMLCatalogCodeLensParticipant.java rename to org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/participants/XMLCatalogCodeLensParticipant.java index 8952cd96e..b9eb0ca1e 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/XMLCatalogCodeLensParticipant.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/participants/XMLCatalogCodeLensParticipant.java @@ -7,7 +7,7 @@ * Contributors: * Red Hat Inc. - initial API and implementation *******************************************************************************/ -package org.eclipse.lemminx.extensions.catalog; +package org.eclipse.lemminx.extensions.catalog.participants; import java.util.Arrays; import java.util.List; diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/XMLCatalogDiagnosticsParticipant.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/participants/XMLCatalogDiagnosticsParticipant.java similarity index 86% rename from org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/XMLCatalogDiagnosticsParticipant.java rename to org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/participants/XMLCatalogDiagnosticsParticipant.java index f2bea7df6..9ab3d47f9 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/XMLCatalogDiagnosticsParticipant.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/participants/XMLCatalogDiagnosticsParticipant.java @@ -8,12 +8,15 @@ * Red Hat Inc. - initial API and implementation *******************************************************************************/ -package org.eclipse.lemminx.extensions.catalog; +package org.eclipse.lemminx.extensions.catalog.participants; import java.text.MessageFormat; import java.util.List; import org.eclipse.lemminx.dom.DOMDocument; +import org.eclipse.lemminx.extensions.catalog.CatalogEntry; +import org.eclipse.lemminx.extensions.catalog.CatalogUtils; +import org.eclipse.lemminx.extensions.catalog.XMLCatalogErrorCode; import org.eclipse.lemminx.extensions.contentmodel.settings.XMLValidationSettings; import org.eclipse.lemminx.services.extensions.diagnostics.IDiagnosticsParticipant; import org.eclipse.lemminx.utils.DOMUtils; @@ -45,7 +48,7 @@ public void doDiagnostics(DOMDocument xmlDocument, List diagnostics, // appending it in the case when original URI does not start with 'file://'. // Ex: originalURI ="foo/bar.xsd" -> path ="file://foo/bar.xsd" String path = CatalogUtils.getResolvedLocation(xmlDocument, catalogEntry); - if (!FilesUtils.isValidPath(FilesUtils.getPath(path)) && URIUtils.isFileResource(path)) { + if (path != null && !FilesUtils.isValidPath(FilesUtils.getPath(path)) && URIUtils.isFileResource(path)) { Range range = XMLPositionUtility.selectValueWithoutQuote(catalogEntry.getLinkRange()); String msg = MessageFormat.format(ERROR_STRING, catalogEntry.getResolvedURI()); diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/XMLCatalogDocumentLinkParticipant.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/participants/XMLCatalogDocumentLinkParticipant.java similarity index 92% rename from org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/XMLCatalogDocumentLinkParticipant.java rename to org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/participants/XMLCatalogDocumentLinkParticipant.java index 9e8faae7e..119b71174 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/XMLCatalogDocumentLinkParticipant.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/catalog/participants/XMLCatalogDocumentLinkParticipant.java @@ -8,7 +8,7 @@ * Red Hat Inc. - initial API and implementation *******************************************************************************/ -package org.eclipse.lemminx.extensions.catalog; +package org.eclipse.lemminx.extensions.catalog.participants; import java.util.List; import java.util.logging.Level; @@ -16,6 +16,8 @@ import org.eclipse.lemminx.commons.BadLocationException; import org.eclipse.lemminx.dom.DOMDocument; +import org.eclipse.lemminx.extensions.catalog.CatalogEntry; +import org.eclipse.lemminx.extensions.catalog.CatalogUtils; import org.eclipse.lemminx.services.extensions.IDocumentLinkParticipant; import org.eclipse.lemminx.utils.XMLPositionUtility; import org.eclipse.lsp4j.DocumentLink; diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/FilePathPlugin.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/FilePathPlugin.java new file mode 100644 index 000000000..6a56898ec --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/FilePathPlugin.java @@ -0,0 +1,152 @@ +/******************************************************************************* +* Copyright (c) 2019 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.filepath; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.lemminx.dom.DOMDocument; +import org.eclipse.lemminx.extensions.filepath.participants.FilePathCompletionParticipant; +import org.eclipse.lemminx.extensions.filepath.settings.FilePathExpression; +import org.eclipse.lemminx.extensions.filepath.settings.FilePathMapping; +import org.eclipse.lemminx.extensions.filepath.settings.FilePathSupportSettings; +import org.eclipse.lemminx.services.extensions.IXMLExtension; +import org.eclipse.lemminx.services.extensions.XMLExtensionsRegistry; +import org.eclipse.lemminx.services.extensions.save.ISaveContext; +import org.eclipse.lsp4j.InitializeParams; + +/** + * File Path support plugin. + */ +public class FilePathPlugin implements IXMLExtension { + + private static final Logger LOGGER = Logger.getLogger(FilePathPlugin.class.getName()); + + private final FilePathCompletionParticipant completionParticipant; + private FilePathSupportSettings filePathsSettings; + + private List filePathSupportParticipants; + + public FilePathPlugin() { + completionParticipant = new FilePathCompletionParticipant(this); + } + + @Override + public void doSave(ISaveContext context) { + if (context.getType() != ISaveContext.SaveContextType.DOCUMENT) { + // Settings + updateSettings(context); + } + } + + private void updateSettings(ISaveContext saveContext) { + Object initializationOptionsSettings = saveContext.getSettings(); + FilePathSupportSettings settings = FilePathSupportSettings + .getFilePathsSettings(initializationOptionsSettings); + updateSettings(settings, saveContext); + } + + private void updateSettings(FilePathSupportSettings settings, ISaveContext context) { + this.filePathsSettings = settings; + } + + @Override + public void start(InitializeParams params, XMLExtensionsRegistry registry) { + registry.registerCompletionParticipant(completionParticipant); + } + + @Override + public void stop(XMLExtensionsRegistry registry) { + registry.unregisterCompletionParticipant(completionParticipant); + } + + public FilePathSupportSettings getFilePathsSettings() { + return filePathsSettings; + } + + /** + * Return the list of {@link FilePathExpression} for the given document and an + * empty list otherwise. + * + * @param xmlDocument the DOM document + * + * @return the list of {@link FilePathExpression} for the given document and an + * empty list otherwise. + */ + public List findFilePathExpressions(DOMDocument xmlDocument) { + List expressions = new ArrayList<>(); + fillFromParticipants(xmlDocument, expressions); + fillFromUserSettings(xmlDocument, expressions); + return expressions; + } + + private void fillFromParticipants(DOMDocument xmlDocument, List allExpressions) { + for (IFilePathSupportParticipant participant : getFilePathSupportParticipants()) { + List expressions = participant.collectFilePathExpressions(xmlDocument); + if (expressions != null && !expressions.isEmpty()) { + allExpressions.addAll(expressions); + } + } + } + + private void fillFromUserSettings(DOMDocument xmlDocument, List expressions) { + FilePathSupportSettings settings = getFilePathsSettings(); + if (settings == null) { + return; + } + + List mappings = settings.getFilePathMappings(); + fillFromMappings(xmlDocument, mappings, expressions); + } + + private static void fillFromMappings(DOMDocument xmlDocument, List mappings, + List expressions) { + if (mappings == null) { + return; + } + + for (FilePathMapping filePaths : mappings) { + if (filePaths.matches(xmlDocument.getDocumentURI())) { + expressions.addAll(filePaths.getExpressions()); + } + } + } + + public List getFilePathSupportParticipants() { + if (filePathSupportParticipants == null) { + loadFilePathSupportParticipants(); + } + return filePathSupportParticipants; + } + + private synchronized void loadFilePathSupportParticipants() { + if (filePathSupportParticipants != null) { + return; + } + List participants = new ArrayList<>(); + Iterator extensions = ServiceLoader.load(IFilePathSupportParticipant.class) + .iterator(); + while (extensions.hasNext()) { + try { + participants.add(extensions.next()); + } catch (ServiceConfigurationError e) { + LOGGER.log(Level.SEVERE, "Error while instantiating file path support participant", e); + } + } + filePathSupportParticipants = participants; + } +} \ No newline at end of file diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/IFilePathExpression.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/IFilePathExpression.java new file mode 100644 index 000000000..1ac3b0113 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/IFilePathExpression.java @@ -0,0 +1,56 @@ +/******************************************************************************* +* Copyright (c) 2023 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.filepath; + +import java.nio.file.Path; + +import org.w3c.dom.Node; + +/** + * File path support expression API. + */ +public interface IFilePathExpression { + + /** + * Returns true if the given DOM node matches the file path expression and false + * otherwise. + * + * @param node the DOM node. + * + * @return true if the given DOM node matches the file path expression and false + * otherwise. + */ + boolean match(Node node); + + /** + * Returns the separator character (ex: ';') used to separate multiple files + * declaration (ex: + * file1.xml;file2.xml) and null otherwise. + * + * @return the separator character (ex: ';') used to separate multiple files + * declaration (ex: + * file1.xml;file2.xml) and null otherwise. + */ + Character getSeparator(); + + /** + * Returns true if given file path is allowed for the file path completion and + * false otherwise. + * + * @param path the file path. + * + * @return true if given file path is allowed for the file path completion and + * false otherwise. + */ + boolean acceptPath(Path path); + +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/IFilePathSupportParticipant.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/IFilePathSupportParticipant.java new file mode 100644 index 000000000..e68b91996 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/IFilePathSupportParticipant.java @@ -0,0 +1,39 @@ +/******************************************************************************* +* Copyright (c) 2023 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.filepath; + +import java.util.List; + +import org.eclipse.lemminx.dom.DOMDocument; + +/** + * File path support participant API. + * + *

+ * This API provides the capability to contribute to + * mark some DOM nodes as file type to have file path completion inside the DOM + * node. + *

+ */ +public interface IFilePathSupportParticipant { + + /** + * Returns the file path expressions used to mark DOM nodes as file type for the + * given DOM document and null or empty otherwise. + * + * @param document the DOM document. + * + * @return the file path expressions used to mark DOM nodes as file type for the + * given DOM document and null or empty otherwise. + */ + List collectFilePathExpressions(DOMDocument document); +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/SimpleFilePathExpression.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/SimpleFilePathExpression.java new file mode 100644 index 000000000..9449ca504 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/SimpleFilePathExpression.java @@ -0,0 +1,46 @@ +/******************************************************************************* +* Copyright (c) 2023 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.filepath; + +import java.nio.file.Files; +import java.nio.file.Path; + +import org.w3c.dom.Node; + +/** + * Basic implementation of the {@link IFilePathExpression}. + */ +public class SimpleFilePathExpression implements IFilePathExpression { + + @Override + public boolean match(Node node) { + return true; + } + + @Override + public Character getSeparator() { + return null; + } + + @Override + public boolean acceptPath(Path path) { + if (!Files.isDirectory(path)) { + return acceptFile(path); + } + return true; + } + + protected boolean acceptFile(Path path) { + return false; + } + +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionParticipant.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionParticipant.java new file mode 100644 index 000000000..9aed9f3b6 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionParticipant.java @@ -0,0 +1,259 @@ +/******************************************************************************* +* Copyright (c) 2023 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.filepath.participants; + +import static org.eclipse.lemminx.utils.FilesUtils.getFileName; +import static org.eclipse.lemminx.utils.platform.Platform.isWindows; + +import java.io.File; +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.lemminx.dom.DOMAttr; +import org.eclipse.lemminx.dom.DOMDocument; +import org.eclipse.lemminx.dom.DOMElement; +import org.eclipse.lemminx.dom.DOMNode; +import org.eclipse.lemminx.dom.DOMRange; +import org.eclipse.lemminx.dom.DOMText; +import org.eclipse.lemminx.dom.DTDDeclParameter; +import org.eclipse.lemminx.dom.NoNamespaceSchemaLocation; +import org.eclipse.lemminx.dom.SchemaLocation; +import org.eclipse.lemminx.extensions.filepath.FilePathPlugin; +import org.eclipse.lemminx.extensions.filepath.IFilePathExpression; +import org.eclipse.lemminx.extensions.filepath.SimpleFilePathExpression; +import org.eclipse.lemminx.services.extensions.completion.CompletionParticipantAdapter; +import org.eclipse.lemminx.services.extensions.completion.ICompletionRequest; +import org.eclipse.lemminx.services.extensions.completion.ICompletionResponse; +import org.eclipse.lemminx.utils.CompletionSortTextHelper; +import org.eclipse.lemminx.utils.DOMUtils; +import org.eclipse.lemminx.utils.FilesUtils; +import org.eclipse.lemminx.utils.XMLPositionUtility; +import org.eclipse.lsp4j.CompletionItem; +import org.eclipse.lsp4j.CompletionItemKind; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.TextEdit; +import org.eclipse.lsp4j.jsonrpc.CancelChecker; +import org.eclipse.lsp4j.jsonrpc.messages.Either; + +/** + * Extension to support completion for file, folder path in: + * + *
    + *
  • attribute value: + * + *
    + * <item path="file:///C:/folder" />
    + * <item path="file:///C:/folder file:///C:/file.txt" />
    + * <item path="/folder" />
    + * 
    + * + *
  • + *
  • DTD DOCTYPE SYSTEM + * + *
    + * <!DOCTYPE parent SYSTEM "file.dtd">
    + * 
    + * + *
  • + * + *
+ * + *

+ * + *

+ */ +public class FilePathCompletionParticipant extends CompletionParticipantAdapter { + + private static final Logger LOGGER = Logger.getLogger(FilePathCompletionParticipant.class.getName()); + + private static final IFilePathExpression DOCTYPE_FILE_PATH_EXPRESSION = new SimpleFilePathExpression() { + + @Override + protected boolean acceptFile(Path path) { + return DOMUtils.isDTD(getFileName(path)); + }; + }; + + private static final IFilePathExpression NO_NAMESPACE_SCHEMALOCATION_FILE_PATH_EXPRESSION = new SimpleFilePathExpression() { + + @Override + protected boolean acceptFile(Path path) { + return DOMUtils.isXSD(getFileName(path)); + }; + }; + + private static final IFilePathExpression SCHEMALOCATION_FILE_PATH_EXPRESSION = new SimpleFilePathExpression() { + + @Override + protected boolean acceptFile(Path path) { + return DOMUtils.isXSD(getFileName(path)); + }; + + public Character getSeparator() { + return ' '; + }; + }; + + private final FilePathPlugin filePathPlugin; + + public FilePathCompletionParticipant(FilePathPlugin filePathPlugin) { + this.filePathPlugin = filePathPlugin; + } + + @Override + public void onAttributeValue(String value, ICompletionRequest request, ICompletionResponse response, + CancelChecker cancelChecker) throws Exception { + DOMDocument document = request.getXMLDocument(); + int completionOffset = request.getOffset(); + DOMNode node = request.getNode(); + DOMAttr attr = node.findAttrAt(request.getOffset()); + + // Check if completion is triggered in the value of + // xsi:noNamespaceSchemaLocation + NoNamespaceSchemaLocation noNamespaceSchemaLocation = document.getNoNamespaceSchemaLocation(); + if (noNamespaceSchemaLocation != null && attr == noNamespaceSchemaLocation.getAttr()) { + addFileCompletionItems(attr, document, completionOffset, NO_NAMESPACE_SCHEMALOCATION_FILE_PATH_EXPRESSION, + response, cancelChecker); + return; + } + + // Check if completion is triggered in the value of xsi:schemaLocation + SchemaLocation schemaLocation = document.getSchemaLocation(); + if (schemaLocation != null && attr == schemaLocation.getAttr()) { + addFileCompletionItems(attr, document, completionOffset, SCHEMALOCATION_FILE_PATH_EXPRESSION, + response, cancelChecker); + return; + } + + // File path completion on attribute value + List expressions = filePathPlugin.findFilePathExpressions(document); + if (expressions.isEmpty()) { + return; + } + + for (IFilePathExpression expression : expressions) { + if (expression.match(attr)) { + cancelChecker.checkCanceled(); + addFileCompletionItems(attr, document, completionOffset, expression, response, cancelChecker); + } + } + } + + private void addFileCompletionItems(DOMAttr attr, DOMDocument xmlDocument, int completionOffset, + IFilePathExpression expression, ICompletionResponse response, CancelChecker cancelChecker) + throws Exception { + DOMRange attrValueRange = attr.getNodeAttrValue(); + addFileCompletionItems(xmlDocument, attrValueRange.getStart() + 1 /* increment to be after the quote */, + attrValueRange.getEnd() - 1, completionOffset, expression, + response, cancelChecker); + } + + @Override + public void onXMLContent(ICompletionRequest request, ICompletionResponse response, CancelChecker cancelChecker) + throws Exception { + // File path completion on text node + List expressions = filePathPlugin.findFilePathExpressions(request.getXMLDocument()); + if (expressions.isEmpty()) { + return; + } + DOMText textNode = findTextNode(request.getNode(), request.getOffset()); + if (textNode == null) { + return; + } + DOMDocument xmlDocument = request.getXMLDocument(); + for (IFilePathExpression expression : expressions) { + if (expression.match(textNode)) { + DOMRange textRange = textNode; + addFileCompletionItems(xmlDocument, textRange.getStart(), textRange.getEnd(), request.getOffset(), + expression, response, cancelChecker); + } + } + } + + private static DOMText findTextNode(DOMNode node, int offset) { + if (node == null || node.isText()) { + return (DOMText) node; + } + if (node.isElement()) { + DOMText text = ((DOMElement) node).findTextAt(offset); + if (text != null) { + return text; + } + } + return null; + } + + @Override + public void onDTDSystemId(String value, ICompletionRequest request, ICompletionResponse response, + CancelChecker cancelChecker) throws Exception { + // File path completion on DTD DOCTYPE SYSTEM + DOMDocument xmlDocument = request.getXMLDocument(); + DTDDeclParameter systemId = xmlDocument.getDoctype().getSystemIdNode(); + addFileCompletionItems(xmlDocument, systemId.getStart() + 1 /* increment to be after the quote */, + systemId.getEnd() - 1, request.getOffset(), DOCTYPE_FILE_PATH_EXPRESSION, response, cancelChecker); + } + + private static void addFileCompletionItems(DOMDocument xmlDocument, int startOffset, int endOffset, + int completionOffset, IFilePathExpression expression, ICompletionResponse response, + CancelChecker cancelChecker) throws Exception { + // Get the resolved base dir of the file path declared insidet startOffset and + // endOffset + // ex: + // base dir is equals for instance to C://path/to + Character separator = expression != null ? expression.getSeparator() : null; + FilePathCompletionResult result = FilePathCompletionResult.create(xmlDocument.getText(), + xmlDocument.getDocumentURI(), startOffset, endOffset, completionOffset, separator); + Path baseDir = result.getBaseDir(); + if (baseDir == null) { + // The base dir cannot be resolved, stop file completion + return; + } + // Fill completions with files / directories of the resolved base dir. + Range replaceRange = XMLPositionUtility.createRange(result.getStartOffset(), result.getEndOffset(), + xmlDocument); + try (DirectoryStream stream = Files.newDirectoryStream(baseDir)) { + for (Path entry : stream) { + cancelChecker.checkCanceled(); + if (expression == null || expression.acceptPath(entry)) { + createFilePathCompletionItem(entry.toFile(), replaceRange, response); + } + } + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "Error while getting files/directories", e); + } + } + + private static void createFilePathCompletionItem(File file, Range replaceRange, ICompletionResponse response) { + CompletionItem item = new CompletionItem(); + String fileName = FilesUtils.encodePath(file.getName()); + if (isWindows && fileName.isEmpty()) { // Edge case for Windows drive letter + fileName = file.getPath(); + fileName = fileName.substring(0, fileName.length() - 1); + } + String insertText = fileName; + item.setLabel(insertText); + + CompletionItemKind kind = file.isDirectory() ? CompletionItemKind.Folder : CompletionItemKind.File; + item.setKind(kind); + + item.setSortText(CompletionSortTextHelper.getSortText(kind)); + item.setFilterText(insertText); + item.setTextEdit(Either.forLeft(new TextEdit(replaceRange, insertText))); + response.addCompletionItem(item); + } +} \ No newline at end of file diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionResult.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionResult.java new file mode 100644 index 000000000..174e55b8b --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionResult.java @@ -0,0 +1,177 @@ +/******************************************************************************* +* Copyright (c) 2023 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.filepath.participants; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.function.Predicate; + +import org.eclipse.lemminx.utils.FilesUtils; +import org.eclipse.lemminx.utils.StringUtils; + +/** + * File path completion result information. + */ +public class FilePathCompletionResult { + + private static final Predicate isStartValidCharForSimplePath = (c) -> c != '/' && c != '\\'; + + private final int startOffset; + + private final int endOffset; + + private final Path baseDir; + + public FilePathCompletionResult(int startOffset, int endOffset, Path baseDir) { + super(); + this.startOffset = startOffset; + this.endOffset = endOffset; + this.baseDir = baseDir; + } + + /** + * Returns the start offset of the last path declared (ex : + * Ex : + *

+ * + *

+ * the method will return 'file://C:/path/to' + * + *

+ * + * @return the resolved base directory of the declared path. + */ + public Path getBaseDir() { + return baseDir; + } + + /** + * Create the file path completion result. + * + * @param content the xml content. + * @param fileUri the file Uri. + * @param startNodeOffset the start node offset where file path is declared. + * @param endNodeOffset the end node offset where file path is declared. + * @param completionOffset the completion offset. + * @param separator the separator used to declare multiple files and null + * otherwise. + * @return the file path completion result. + */ + public static FilePathCompletionResult create(String content, String fileUri, int startNodeOffset, + int endNodeOffset, int completionOffset, Character separator) { + boolean isMultiFilePath = separator != null; + Predicate isStartValidChar = isStartValidCharForSimplePath; + int endPathOffset = endNodeOffset; + if (isMultiFilePath) { + // multiple file path (ex :
+ isStartValidChar = c -> c != separator && isStartValidCharForSimplePath.test(c); + endPathOffset = StringUtils.findEndWord(content, completionOffset -1, endNodeOffset, c -> c != separator); + if (endPathOffset == -1) { + endPathOffset = endNodeOffset; + } + } + int startPathOffset = StringUtils.findStartWord(content, completionOffset, startNodeOffset, isStartValidChar); + int startBaseDirOffset = startNodeOffset; + if (isMultiFilePath && !isStartValidChar.test(content.charAt(startPathOffset - 1))) { + // multiple file path (ex : + int tmp = StringUtils.findStartWord(content, completionOffset, startNodeOffset, c -> c != separator); + if (tmp != -1) { + startBaseDirOffset = tmp; + } + } + + Path baseDir = getBaseDir(content, fileUri, startBaseDirOffset, startPathOffset); + if (baseDir == null || !Files.exists(baseDir)) { + baseDir = null; + } + return new FilePathCompletionResult(startPathOffset, endPathOffset, baseDir); + } + + private static Path getBaseDir(String content, String fileUri, int start, int end) { + if (end > start) { + // ex : + String basePath = content.substring(start, end); + if (!hasPathBeginning(basePath)) { + // Try to returns the absolute path + // Ex basePath= + // - path/to/file.xml + // - C://path/to/file.xml + try { + Path baseDir = FilesUtils.getPath(basePath); + if (Files.exists(baseDir)) { + // returns C://path/to + return baseDir; + } + } catch (Exception e) { + + } + } + // Resolve the relative path by using the parent folder of the document file + // Uri.: + // Ex basePath= + // - path/to/file.xml + // - ./path/to/file.xml + // - ../path/to/file.xml + try { + // returns C://path/to + return FilesUtils.getPath(fileUri).getParent().resolve(basePath); + } catch (Exception e) { + return null; + } + } + return FilesUtils.getPath(fileUri).getParent(); + } + + private static boolean hasPathBeginning(String currentText) { + if (currentText.startsWith("/") + || currentText.startsWith("./") + || currentText.startsWith("../") + || currentText.startsWith("..\\") + || currentText.startsWith(".\\")) { + return true; + } + return isAbsoluteWindowsPath(currentText); + } + + private static boolean isAbsoluteWindowsPath(String currentText) { + if (currentText.length() < 3) { + return false; + } + if (!Character.isLetter(currentText.charAt(0))) { + return false; + } + return currentText.charAt(1) == ':' && (currentText.charAt(2) == '\\' || currentText.charAt(2) == '/'); + } + +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/settings/FilePathExpression.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/settings/FilePathExpression.java new file mode 100644 index 000000000..d497668f7 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/settings/FilePathExpression.java @@ -0,0 +1,106 @@ +/******************************************************************************* +* Copyright (c) 2023 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.filepath.settings; + +import static org.eclipse.lemminx.utils.FilesUtils.getFileName; + +import java.nio.file.Path; +import java.util.List; + +import org.eclipse.lemminx.extensions.filepath.SimpleFilePathExpression; +import org.eclipse.lemminx.xpath.matcher.XPathMatcher; +import org.w3c.dom.Node; + +/** + * File path expression + * + * + * { + "xpath": "@paths", + "separator": " " + } + * + * + * @author Angelo ZERR + * + */ +public class FilePathExpression extends SimpleFilePathExpression { + + private transient XPathMatcher pathMatcher; + + private String xpath; + + private Character separator; + + private List filter; + + public FilePathExpression() { + this(null); + } + + public FilePathExpression(String xpath) { + setXPath(xpath); + } + + public String getXPath() { + return xpath; + } + + @Override + public Character getSeparator() { + return separator; + } + + public List getFilter() { + return filter; + } + + public FilePathExpression setXPath(String xpath) { + this.xpath = xpath; + return this; + } + + public FilePathExpression setSeparator(Character separator) { + this.separator = separator; + return this; + } + + public FilePathExpression setFilter(List filter) { + this.filter = filter; + return this; + } + + @Override + public boolean match(final Node node) { + if (xpath == null) { + return false; + } + if (pathMatcher == null) { + pathMatcher = new XPathMatcher(xpath); + } + return pathMatcher.match(node); + } + + @Override + protected boolean acceptFile(Path path) { + if (filter == null || filter.isEmpty()) { + return true; + } + String fileName = getFileName(path); + for (String fileExtension : filter) { + if (fileName.endsWith(fileExtension)) { + return true; + } + } + return false; + } +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/settings/FilePathMapping.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/settings/FilePathMapping.java new file mode 100644 index 000000000..7722c7d69 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/settings/FilePathMapping.java @@ -0,0 +1,46 @@ +/******************************************************************************* +* Copyright (c) 2023 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.filepath.settings; + +import java.util.List; + +import org.eclipse.lemminx.settings.PathPatternMatcher; + +/** + * File path mapping which stores list of {@link FilePathExpression} applied + * for a given input file pattern. + * + * @author Angelo ZERR + * + */ +public class FilePathMapping extends PathPatternMatcher { + + private List expressions; + + /** + * Returns list of file path expressions. + * + * @return list of file path expressions. + */ + public List getExpressions() { + return expressions; + } + + /** + * Set list of file path expressions. + * + * @param expressions list of file path expressions. + */ + public void setExpressions(List expressions) { + this.expressions = expressions; + } +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/settings/FilePathSupport.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/settings/FilePathSupport.java new file mode 100644 index 000000000..b47dc19fb --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/settings/FilePathSupport.java @@ -0,0 +1,30 @@ +/******************************************************************************* +* Copyright (c) 2023 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.filepath.settings; + +import java.util.List; + +/** + * File path support settings. + */ +public class FilePathSupport { + + private List mappings; + + public List getMappings() { + return mappings; + } + + public void setMappings(List mappings) { + this.mappings = mappings; + } +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/settings/FilePathSupportSettings.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/settings/FilePathSupportSettings.java new file mode 100644 index 000000000..f19628ba0 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/settings/FilePathSupportSettings.java @@ -0,0 +1,66 @@ +/******************************************************************************* +* Copyright (c) 2023 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.filepath.settings; + +import java.util.List; + +import org.eclipse.lemminx.utils.JSONUtility; + +/** + * File paths settings: + * + * + "xml.filePathSupport.mappings": [ + // File paths applied for text node items.xml files + { + "pattern": "*.xml", + "expressions": [ + { + "xpath": "path/text()" + }, + { + "xpath": "item/@path" + }, + { + "xpath": "item/@paths", + "separator": " " + } + ] + } +] + * + * + * + * @author Angelo ZERR + * + */ +public class FilePathSupportSettings { + + private FilePathSupport filePathSupport; + + public FilePathSupport getFilePathSupport() { + return filePathSupport; + } + + public void setFilePathSupport(FilePathSupport filePathSupport) { + this.filePathSupport = filePathSupport; + } + + public static FilePathSupportSettings getFilePathsSettings(Object initializationOptionsSettings) { + return JSONUtility.toModel(initializationOptionsSettings, FilePathSupportSettings.class); + } + + public List getFilePathMappings() { + return filePathSupport != null ? filePathSupport.getMappings() : null; + } + +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/general/FilePathPlugin.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/general/FilePathPlugin.java deleted file mode 100644 index 5897c5cc7..000000000 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/general/FilePathPlugin.java +++ /dev/null @@ -1,40 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2019 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.general; - -import org.eclipse.lemminx.extensions.general.completion.FilePathCompletionParticipant; -import org.eclipse.lemminx.services.extensions.IXMLExtension; -import org.eclipse.lemminx.services.extensions.XMLExtensionsRegistry; -import org.eclipse.lsp4j.InitializeParams; - -/** - * FilePathPlugin - */ -public class FilePathPlugin implements IXMLExtension { - - private final FilePathCompletionParticipant completionParticipant; - - public FilePathPlugin() { - completionParticipant = new FilePathCompletionParticipant(); - } - - @Override - public void start(InitializeParams params, XMLExtensionsRegistry registry) { - registry.registerCompletionParticipant(completionParticipant); - } - - @Override - public void stop(XMLExtensionsRegistry registry) { - registry.unregisterCompletionParticipant(completionParticipant); - } - -} \ No newline at end of file diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/general/completion/FilePathCompletionParticipant.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/general/completion/FilePathCompletionParticipant.java deleted file mode 100644 index d996c56de..000000000 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/general/completion/FilePathCompletionParticipant.java +++ /dev/null @@ -1,286 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2019 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.general.completion; - -import static org.eclipse.lemminx.utils.FilesUtils.getFilePathSlash; -import static org.eclipse.lemminx.utils.StringUtils.isEmpty; -import static org.eclipse.lemminx.utils.platform.Platform.isWindows; - -import java.io.File; -import java.io.FilenameFilter; -import java.io.IOException; -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.eclipse.lemminx.commons.BadLocationException; -import org.eclipse.lemminx.dom.DOMDocument; -import org.eclipse.lemminx.services.extensions.completion.CompletionParticipantAdapter; -import org.eclipse.lemminx.services.extensions.completion.ICompletionRequest; -import org.eclipse.lemminx.services.extensions.completion.ICompletionResponse; -import org.eclipse.lemminx.utils.CompletionSortTextHelper; -import org.eclipse.lemminx.utils.FilesUtils; -import org.eclipse.lemminx.utils.StringUtils; -import org.eclipse.lemminx.utils.XMLPositionUtility; -import org.eclipse.lsp4j.CompletionItem; -import org.eclipse.lsp4j.CompletionItemKind; -import org.eclipse.lsp4j.Position; -import org.eclipse.lsp4j.Range; -import org.eclipse.lsp4j.TextEdit; -import org.eclipse.lsp4j.jsonrpc.CancelChecker; -import org.eclipse.lsp4j.jsonrpc.messages.Either; - -/** - * Extension to support completion for file, folder path in: - * - *
    - *
  • attribute value: - * - *
    - * <item path="file:///C:/folder" />
    - * <item path="file:///C:/folder file:///C:/file.txt" />
    - * <item path="/folder" />
    - * 
    - * - *
  • - *
  • DTD DOCTYPE SYSTEM - * - *
    - * <!DOCTYPE parent SYSTEM "file.dtd">
    - * 
    - * - *
  • - * - *
- * - *

- * - *

- */ -public class FilePathCompletionParticipant extends CompletionParticipantAdapter { - - private static final Logger LOGGER = Logger.getLogger(FilePathCompletionParticipant.class.getName()); - - @Override - public void onAttributeValue(String value, ICompletionRequest request, ICompletionResponse response, - CancelChecker cancelChecker) throws Exception { - // File path completion on attribute value - addCompletionItems(value, request, response, false); - } - - @Override - public void onDTDSystemId(String value, ICompletionRequest request, ICompletionResponse response, - CancelChecker cancelChecker) throws Exception { - // File path completion on DTD DOCTYPE SYSTEM - addCompletionItems(value, request, response, true); - } - - private static void addCompletionItems(String value, ICompletionRequest request, ICompletionResponse response, boolean isInDoctype) - throws Exception { - String fullValue = value; - if (isEmpty(fullValue) && !isInDoctype) { - return; - } - - DOMDocument xmlDocument = request.getXMLDocument(); - String text = xmlDocument.getText(); - - // Get value and range for file path declared inside the attribute value - // ex value="file:///C:/fold|er" - int valuePathStartOffset = xmlDocument.offsetAt(request.getReplaceRange().getStart()); - int endPathOffset = request.getOffset(); // offset after the typed character - int startPathOffset = (fullValue.length() == 0 ? 0 : StringUtils.getOffsetAfterWhitespace(fullValue, endPathOffset - valuePathStartOffset)) - + valuePathStartOffset; // first character of URI - Range filePathRange = XMLPositionUtility.createRange(startPathOffset, endPathOffset, xmlDocument); - String originalValuePath = text.substring(startPathOffset, endPathOffset); - // ex: valuePath="file:///C:/fold" - String valuePath = originalValuePath; - String slashInAttribute = getFilePathSlash(valuePath); - - boolean hasFileScheme = valuePath.startsWith(FilesUtils.FILE_SCHEME); - if (hasFileScheme) { - // remove file:// scheme - // ex: valuePath="/C:/fold" - valuePath = FilesUtils.removeFileScheme(valuePath); - if (valuePath.length() == 0 || valuePath.charAt(0) != '/') { - // use of 'file://' and the path was not absolute - return; - } - if (isWindows) { - // For Windows OS, remove the last '/' from file:/// - // ex: valuePath="C:/fold" - valuePath = valuePath.substring(1, valuePath.length()); - if (valuePath.length() == 1) { - // only '/', so list Windows Drives - Range replaceRange = adjustReplaceRange(xmlDocument, filePathRange, originalValuePath, "/"); - File[] drives = File.listRoots(); - for (File drive : drives) { - createFilePathCompletionItem(drive, replaceRange, response, "/"); - } - return; - } - } - } else if (!hasPathBeginning(valuePath) && !isInDoctype) { - // the user probably didn't intend to complete a path - return; - } - // On Linux, Mac OS replace '\\' with '/' - if (!isWindows) { - if ("\\".equals(slashInAttribute)) { // Backslash used in Unix - valuePath = valuePath.replace("\\", "/"); - } - } - - // Get IO path from the given value path - Path validAttributePath = getPath(valuePath, xmlDocument.getTextDocument().getUri()); - if (validAttributePath == null) { - return; - } - - // Get adjusted range for the completion item (insert at end, or overwrite some - // existing text in the path) - Range replaceRange = adjustReplaceRange(xmlDocument, filePathRange, originalValuePath, slashInAttribute); - createNextValidCompletionPaths(validAttributePath, slashInAttribute, replaceRange, response, null); - } - - /** - * Returns the IO Path from the given value path. - * - * @param valuePath the value path - * @param xmlFileUri the XML file URI where completion has been triggered. - * @return the IO Path from the given value path. - */ - private static Path getPath(String valuePath, String xmlFileUri) { - // the value path is the filepath URI without file:// - try { - Path validAttributePath = FilesUtils.getPath(valuePath); - if (!validAttributePath.isAbsolute()) { - // Absolute path, use the XML file URI folder as base dirctory. - Path workingDirectoryPath = FilesUtils.getPath(xmlFileUri).getParent(); - validAttributePath = workingDirectoryPath.resolve(validAttributePath).normalize(); - } - if (!".".equals(valuePath) && !valuePath.endsWith("/") && !valuePath.endsWith("\\")) { - // ex : C:/folder|/ -> in this case the path is the folder parent (C:) - validAttributePath = validAttributePath.getParent(); - } - return Files.exists(validAttributePath) ? validAttributePath : null; - } catch (Exception e) { - return null; - } - } - - /** - * Returns a Range that covers trailing content after a slash, or if it already - * ends with a slash then a Range right after it. - * - * @param xmlDocument - * @param fullRange - * @param attributeValue - * @param slash - * @return - */ - private static Range adjustReplaceRange(DOMDocument xmlDocument, Range fullRange, String attributeValue, - String slash) { - // In the case the currently typed file/directory needs to be overwritten - Position replaceStart = null; - Position currentEnd = fullRange.getEnd(); - - int startOffset; - try { - startOffset = xmlDocument.offsetAt(fullRange.getStart()); - } catch (BadLocationException e) { - return null; - } - int lastSlashIndex = attributeValue.lastIndexOf(slash); - if (lastSlashIndex > -1) { - try { - replaceStart = xmlDocument.positionAt(startOffset + lastSlashIndex); - } catch (BadLocationException e) { - return null; - } - } - Range replaceRange = new Range(); - if (replaceStart != null) { - replaceRange.setStart(replaceStart); - } else { - replaceRange.setStart(currentEnd); - } - replaceRange.setEnd(currentEnd); - return replaceRange; - } - - /** - * Creates the completion items based off the given absolute path - * - * @param pathToAttributeDirectory - * @param attributePath - * @param replaceRange - * @param response - * @param filter - */ - private static void createNextValidCompletionPaths(Path pathToAttributeDirectory, String slash, Range replaceRange, - ICompletionResponse response, FilenameFilter filter) { - try (DirectoryStream stream = Files.newDirectoryStream(pathToAttributeDirectory)) { - for (Path entry : stream) { - createFilePathCompletionItem(entry.toFile(), replaceRange, response, slash); - } - } catch (IOException e) { - LOGGER.log(Level.SEVERE, "Error while getting files/directories", e); - } - } - - private static void createFilePathCompletionItem(File f, Range replaceRange, ICompletionResponse response, - String slash) { - CompletionItem item = new CompletionItem(); - String fName = FilesUtils.encodePath(f.getName()); - if (isWindows && fName.isEmpty()) { // Edge case for Windows drive letter - fName = f.getPath(); - fName = fName.substring(0, fName.length() - 1); - } - String insertText; - insertText = slash + fName; - item.setLabel(insertText); - - CompletionItemKind kind = f.isDirectory() ? CompletionItemKind.Folder : CompletionItemKind.File; - item.setKind(kind); - - item.setSortText(CompletionSortTextHelper.getSortText(kind)); - item.setFilterText(insertText); - item.setTextEdit(Either.forLeft(new TextEdit(replaceRange, insertText))); - response.addCompletionItem(item); - } - - private static boolean hasPathBeginning(String currentText) { - if (currentText.startsWith("/") - || currentText.startsWith("./") - || currentText.startsWith("../") - || currentText.startsWith("..\\") - || currentText.startsWith(".\\")) { - return true; - } - return isAbsoluteWindowsPath(currentText); - } - - private static boolean isAbsoluteWindowsPath(String currentText) { - if (currentText.length() < 3) { - return false; - } - if (!Character.isLetter(currentText.charAt(0))) { - return false; - } - return currentText.charAt(1) == ':' && (currentText.charAt(2) == '\\' || currentText.charAt(2) == '/'); - } - -} \ No newline at end of file diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/relaxng/grammar/rng/RNGFilePathSupportParticipant.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/relaxng/grammar/rng/RNGFilePathSupportParticipant.java new file mode 100644 index 000000000..153011da7 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/relaxng/grammar/rng/RNGFilePathSupportParticipant.java @@ -0,0 +1,49 @@ +/******************************************************************************* +* Copyright (c) 2023 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.relaxng.grammar.rng; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.eclipse.lemminx.dom.DOMDocument; +import org.eclipse.lemminx.extensions.filepath.IFilePathExpression; +import org.eclipse.lemminx.extensions.filepath.IFilePathSupportParticipant; +import org.eclipse.lemminx.extensions.filepath.settings.FilePathExpression; +import org.eclipse.lemminx.utils.DOMUtils; + +/** + * RelaxNG file path support for *.rng to provide completion for attributes : + * + *
    + *
  • include/@href
  • + *
  • externalRef/@href
  • + *
+ */ +public class RNGFilePathSupportParticipant implements IFilePathSupportParticipant { + + private static final List RNG_FILE_PATH_EXPRESSIONS; + + static { + RNG_FILE_PATH_EXPRESSIONS = Arrays.asList(new FilePathExpression("include/@href"), + new FilePathExpression("externalRef/@href")); + } + + @Override + public List collectFilePathExpressions(DOMDocument document) { + if (!DOMUtils.isRelaxNG(document)) { + return Collections.emptyList(); + } + return RNG_FILE_PATH_EXPRESSIONS; + } + +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsd/participants/XSDDocumentLinkParticipant.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsd/participants/XSDDocumentLinkParticipant.java index e91c73665..58707f6da 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsd/participants/XSDDocumentLinkParticipant.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsd/participants/XSDDocumentLinkParticipant.java @@ -33,8 +33,8 @@ * * Implements document links in .xsd files for *
    - *
  • xs:include/schemaLocation
  • - *
  • xs:import/schemaLocation
  • + *
  • xs:include/@schemaLocation
  • + *
  • xs:import/@schemaLocation
  • *
* */ diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsd/participants/XSDFilePathSupportParticipant.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsd/participants/XSDFilePathSupportParticipant.java new file mode 100644 index 000000000..f19b07555 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsd/participants/XSDFilePathSupportParticipant.java @@ -0,0 +1,64 @@ +/******************************************************************************* +* Copyright (c) 2023 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.xsd.participants; + +import static org.eclipse.lemminx.utils.FilesUtils.getFileName; + +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.eclipse.lemminx.dom.DOMDocument; +import org.eclipse.lemminx.extensions.filepath.IFilePathExpression; +import org.eclipse.lemminx.extensions.filepath.IFilePathSupportParticipant; +import org.eclipse.lemminx.extensions.filepath.settings.FilePathExpression; +import org.eclipse.lemminx.utils.DOMUtils; + +/** + * XML Schema file path support for *.xsd to provide completion for attributes : + * + *
    + *
  • include/@schemaLocation
  • + *
  • import/@schemaLocation
  • + *
+ */ +public class XSDFilePathSupportParticipant implements IFilePathSupportParticipant { + + private static class XSDFilePathExpression extends FilePathExpression { + + public XSDFilePathExpression(String xpath) { + super(xpath); + } + + @Override + protected boolean acceptFile(Path path) { + return DOMUtils.isXSD(getFileName(path)); + } + } + + private static final List XSD_FILE_PATH_EXPRESSIONS; + + static { + XSD_FILE_PATH_EXPRESSIONS = Arrays.asList(new XSDFilePathExpression("include/@schemaLocation"), + new XSDFilePathExpression("import/@schemaLocation")); + } + + @Override + public List collectFilePathExpressions(DOMDocument document) { + if (!DOMUtils.isXSD(document)) { + return Collections.emptyList(); + } + return XSD_FILE_PATH_EXPRESSIONS; + } + +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsl/participants/XSLFilePathSupportParticipant.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsl/participants/XSLFilePathSupportParticipant.java new file mode 100644 index 000000000..10aa825a0 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsl/participants/XSLFilePathSupportParticipant.java @@ -0,0 +1,65 @@ +/******************************************************************************* +* Copyright (c) 2023 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.xsl.participants; + +import static org.eclipse.lemminx.utils.FilesUtils.getFileName; + +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.eclipse.lemminx.dom.DOMDocument; +import org.eclipse.lemminx.extensions.filepath.IFilePathExpression; +import org.eclipse.lemminx.extensions.filepath.IFilePathSupportParticipant; +import org.eclipse.lemminx.extensions.filepath.settings.FilePathExpression; +import org.eclipse.lemminx.utils.DOMUtils; + +/** + * XML Stylesheet file path support for *.xsl to provide completion for + * attributes : + * + *
    + *
  • include/@href
  • + *
  • import/@href
  • + *
+ */ +public class XSLFilePathSupportParticipant implements IFilePathSupportParticipant { + + private static class XSLFilePathExpression extends FilePathExpression { + + public XSLFilePathExpression(String xpath) { + super(xpath); + } + + @Override + protected boolean acceptFile(Path path) { + return DOMUtils.isXSL(getFileName(path)); + } + } + + private static final List XSL_FILE_PATH_EXPRESSIONS; + + static { + XSL_FILE_PATH_EXPRESSIONS = Arrays.asList(new XSLFilePathExpression("include/@href"), + new XSLFilePathExpression("import/@href")); + } + + @Override + public List collectFilePathExpressions(DOMDocument document) { + if (!DOMUtils.isXSL(document)) { + return Collections.emptyList(); + } + return XSL_FILE_PATH_EXPRESSIONS; + } + +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/snippets/NewFileSnippetContext.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/snippets/NewFileSnippetContext.java index ffd1928bd..970de9458 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/snippets/NewFileSnippetContext.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/snippets/NewFileSnippetContext.java @@ -28,7 +28,8 @@ public abstract class NewFileSnippetContext implements IXMLSnippetContext { public static final IXMLSnippetContext XML_CONTEXT = new NewFileSnippetContext() { @Override protected boolean isMatchType(DOMDocument document) { - return !(document.isDTD() || DOMUtils.isXSD(document) || DOMUtils.isRelaxNG(document)); + return !(document.isDTD() || DOMUtils.isXSD(document) || DOMUtils.isRelaxNG(document) + || DOMUtils.isXSL(document)); } }; @@ -53,6 +54,13 @@ protected boolean isMatchType(DOMDocument document) { } }; + public static final IXMLSnippetContext XSL_CONTEXT = new NewFileSnippetContext() { + @Override + protected boolean isMatchType(DOMDocument document) { + return DOMUtils.isXSL(document); + } + }; + @Override public boolean isMatch(ICompletionRequest request, Map model) { DOMDocument document = request.getXMLDocument(); diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/snippets/XMLSnippetRegistryLoader.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/snippets/XMLSnippetRegistryLoader.java index eddea21f8..afff9d966 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/snippets/XMLSnippetRegistryLoader.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/snippets/XMLSnippetRegistryLoader.java @@ -30,6 +30,8 @@ public void load(SnippetRegistry registry) throws Exception { NewFileSnippetContext.XSD_CONTEXT); registry.registerSnippets(XMLSnippetRegistryLoader.class.getResourceAsStream("new-rng-snippets.json"), NewFileSnippetContext.RNG_CONTEXT); + registry.registerSnippets(XMLSnippetRegistryLoader.class.getResourceAsStream("new-xsl-snippets.json"), + NewFileSnippetContext.XSL_CONTEXT); registry.registerSnippets(XMLSnippetRegistryLoader.class.getResourceAsStream("cdata-snippets.json"), CDATASnippetContext.DEFAULT_CONTEXT); registry.registerSnippets(XMLSnippetRegistryLoader.class.getResourceAsStream("comment-snippets.json"), @@ -40,7 +42,8 @@ public void load(SnippetRegistry registry) throws Exception { XMLDeclarationSnippetContext.DEFAULT_CONTEXT); registry.registerSnippets(XMLSnippetRegistryLoader.class.getResourceAsStream("dtdnode-snippets.json"), DTDNodeSnippetContext.DEFAULT_CONTEXT); - registry.registerSnippets(XMLSnippetRegistryLoader.class.getResourceAsStream("processing-instruction-snippets.json"), + registry.registerSnippets( + XMLSnippetRegistryLoader.class.getResourceAsStream("processing-instruction-snippets.json"), ProcessingInstructionSnippetContext.DEFAULT_CONTEXT); } diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/PathPatternMatcher.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/PathPatternMatcher.java index c2fe92081..30bb7aa77 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/PathPatternMatcher.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/PathPatternMatcher.java @@ -26,9 +26,10 @@ public String getPattern() { return pattern; } - public void setPattern(String pattern) { + public PathPatternMatcher setPattern(String pattern) { this.pattern = pattern; this.pathMatcher = null; + return this; } public PathMatcher getPathMatcher() { diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/CompletionItemDefaultsUtils.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/CompletionItemDefaultsUtils.java index 9e6d0214e..3e4d2e1e7 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/CompletionItemDefaultsUtils.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/CompletionItemDefaultsUtils.java @@ -62,7 +62,9 @@ public static void process(CompletionResponse completionResponse, SharedSettings */ private static void setToMostCommonEditRange(List completionList, CompletionItemDefaults itemDefaults) { - Map> itemsByRange = completionList.stream() + Map> itemsByRange = completionList + .stream() + .filter(item -> item.getTextEdit() != null && item.getTextEdit().isLeft()) .collect(Collectors.groupingBy(item -> item.getTextEdit().getLeft().getRange())); int maxCount = 0; Range mostCommonRange = null; @@ -73,12 +75,17 @@ private static void setToMostCommonEditRange(List completionList mostCommonRange = entry.getKey(); } } + if (mostCommonRange == null) { + return; + } itemsByRange.get(mostCommonRange).forEach(item -> { item.setTextEditText(item.getTextEdit().getLeft().getNewText()); item.setTextEdit(null); }); itemDefaults.setEditRange(Either.forLeft(mostCommonRange)); - completionList = itemsByRange.values().stream().flatMap(Collection::stream) + completionList = itemsByRange.values() + .stream() + .flatMap(Collection::stream) .collect(Collectors.toList()); } @@ -91,7 +98,8 @@ private static void setToMostCommonEditRange(List completionList */ private static void setToMostCommonInsertTextFormat(List completionList, CompletionItemDefaults itemDefaults) { - Map> itemsByInsertTextFormat = completionList.stream() + Map> itemsByInsertTextFormat = completionList + .stream() .filter(item -> item.getInsertTextFormat() != null) .collect(Collectors.groupingBy(item -> item.getInsertTextFormat())); int maxCount = 0; diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/DOMUtils.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/DOMUtils.java index 7b2764e6b..cd9120fe3 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/DOMUtils.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/DOMUtils.java @@ -51,6 +51,10 @@ public class DOMUtils { private static final String RNC_EXTENSION = ".rnc"; + private static final String XSL_EXTENSION = ".xsl"; + + private static final String HTTP_WWW_W3_ORG_1999_XSL_TRANSFORM_NS = "http://www.w3.org/1999/XSL/Transform"; + private DOMUtils() { } @@ -189,6 +193,36 @@ public static boolean isDTD(String uri) { && (uri.endsWith(DTD_EXTENSION) || uri.endsWith(ENT_EXTENSION) || uri.endsWith(MOD_EXTENSION)); } + /** + * Returns true if the XML document is a XSL and false otherwise. + * + * @return true if the XML document is a XSL and false otherwise. + */ + public static boolean isXSL(DOMDocument document) { + if (document == null) { + return false; + } + String uri = document.getDocumentURI(); + if (isXSL(uri)) { + return true; + } + // check root element is bound with XSL namespace + // (http://www.w3.org/1999/XSL/Transform) + return checkRootNamespace(document, HTTP_WWW_W3_ORG_1999_XSL_TRANSFORM_NS); + + } + + /** + * Returns true if the given URI is a XSL and false otherwise. + * + * @param uri the URI to check + * @return true if the given URI is a XSL and false otherwise. + */ + public static boolean isXSL(String uri) { + return uri != null + && (uri.endsWith(XSL_EXTENSION)); + } + /** * Returns true if element contains only DOMText and false otherwise. * diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/FilesUtils.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/FilesUtils.java index 6fc3548be..6e02b50dc 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/FilesUtils.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/FilesUtils.java @@ -277,12 +277,13 @@ public static Path getPath(String uri) { public static String encodePath(String path) { return path.replace(" ", "%20"); } - + /** - * Very simple implementation of reading all content from a file into a string using the UTF-8 charset. Uses the System's line separator. - * Not suited for reading large files. + * Very simple implementation of reading all content from a file into a string + * using the UTF-8 charset. Uses the System's line separator. + * Not suited for reading large files. * - * @param path the path to a readable file. + * @param path the path to a readable file. * @return the contents of the file. * @throws IOException if an error occurred while reading the file. */ @@ -295,12 +296,23 @@ public static String readString(Path path) throws IOException { * * @param path the path. * - * @return true if the file at the given path exists. + * @return true if the file at the given path exists. */ - public static boolean isValidPath(Path path){ + public static boolean isValidPath(Path path) { if (Files.exists(path)) { return true; } return false; } + + /** + * Returns the file name of the given path. + * + * @param path the path. + * + * @return the file name of the given path. + */ + public static String getFileName(Path path) { + return path.getName(path.getNameCount() - 1).toString(); + } } diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/StringUtils.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/StringUtils.java index 8562db439..9147ada1a 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/StringUtils.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/StringUtils.java @@ -438,7 +438,7 @@ public static int findStartWord(String text, int offset, Predicate is * to the given min and -1 if no word. */ public static int findStartWord(String text, int offset, int min, Predicate isValidChar) { - if (offset < 0 || offset >= text.length() || !isValidChar.test(text.charAt(offset))) { + if (offset < 0 || offset >= text.length()) { return -1; } for (int i = offset - 1; i >= min; i--) { diff --git a/org.eclipse.lemminx/src/main/resources/META-INF/native-image/reflect-config.json b/org.eclipse.lemminx/src/main/resources/META-INF/native-image/reflect-config.json index daecd3a5a..ac18cc39d 100644 --- a/org.eclipse.lemminx/src/main/resources/META-INF/native-image/reflect-config.json +++ b/org.eclipse.lemminx/src/main/resources/META-INF/native-image/reflect-config.json @@ -481,6 +481,38 @@ "parameterTypes": [] }] }, + { + "name": "org.eclipse.lemminx.extensions.filepath.settings.FilePathExpression", + "allDeclaredFields": true, + "methods": [{ + "name": "", + "parameterTypes": [] + }] + }, + { + "name": "org.eclipse.lemminx.extensions.filepath.settings.FilePathMapping", + "allDeclaredFields": true, + "methods": [{ + "name": "", + "parameterTypes": [] + }] + }, + { + "name": "org.eclipse.lemminx.extensions.filepath.settings.FilePathSupport", + "allDeclaredFields": true, + "methods": [{ + "name": "", + "parameterTypes": [] + }] + }, + { + "name": "org.eclipse.lemminx.extensions.filepath.settings.FilePathSupportSettings", + "allDeclaredFields": true, + "methods": [{ + "name": "", + "parameterTypes": [] + }] + }, { "name": "org.eclipse.lemminx.settings.QuoteStyle", "allDeclaredFields": true diff --git a/org.eclipse.lemminx/src/main/resources/META-INF/services/org.eclipse.lemminx.extensions.filepath.IFilePathSupportParticipant b/org.eclipse.lemminx/src/main/resources/META-INF/services/org.eclipse.lemminx.extensions.filepath.IFilePathSupportParticipant new file mode 100644 index 000000000..cc7109378 --- /dev/null +++ b/org.eclipse.lemminx/src/main/resources/META-INF/services/org.eclipse.lemminx.extensions.filepath.IFilePathSupportParticipant @@ -0,0 +1,4 @@ +org.eclipse.lemminx.extensions.catalog.participants.CatalogFilePathSupportParticipant +org.eclipse.lemminx.extensions.relaxng.grammar.rng.RNGFilePathSupportParticipant +org.eclipse.lemminx.extensions.xsd.participants.XSDFilePathSupportParticipant +org.eclipse.lemminx.extensions.xsl.participants.XSLFilePathSupportParticipant \ No newline at end of file diff --git a/org.eclipse.lemminx/src/main/resources/META-INF/services/org.eclipse.lemminx.services.extensions.IXMLExtension b/org.eclipse.lemminx/src/main/resources/META-INF/services/org.eclipse.lemminx.services.extensions.IXMLExtension index e23229121..abdc794ec 100644 --- a/org.eclipse.lemminx/src/main/resources/META-INF/services/org.eclipse.lemminx.services.extensions.IXMLExtension +++ b/org.eclipse.lemminx/src/main/resources/META-INF/services/org.eclipse.lemminx.services.extensions.IXMLExtension @@ -6,7 +6,7 @@ org.eclipse.lemminx.extensions.xsl.XSLPlugin org.eclipse.lemminx.extensions.catalog.XMLCatalogPlugin org.eclipse.lemminx.extensions.xsi.XSISchemaPlugin org.eclipse.lemminx.extensions.prolog.PrologPlugin -org.eclipse.lemminx.extensions.general.FilePathPlugin +org.eclipse.lemminx.extensions.filepath.FilePathPlugin org.eclipse.lemminx.extensions.entities.EntitiesPlugin org.eclipse.lemminx.extensions.xmlmodel.XMLModelPlugin org.eclipse.lemminx.extensions.generators.FileContentGeneratorPlugin diff --git a/org.eclipse.lemminx/src/main/resources/org/eclipse/lemminx/services/snippets/new-xsl-snippets.json b/org.eclipse.lemminx/src/main/resources/org/eclipse/lemminx/services/snippets/new-xsl-snippets.json new file mode 100644 index 000000000..015c916fd --- /dev/null +++ b/org.eclipse.lemminx/src/main/resources/org/eclipse/lemminx/services/snippets/new-xsl-snippets.json @@ -0,0 +1,16 @@ +{ + "New XSL Stylesheet": { + "prefix": [ + "", + "body": [ + "", + "\t", + "\t", + "" + ], + "label": "$description", + "description": "New XSL Stylesheet" + } + } \ No newline at end of file diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/catalog/FilePathCompletionWithCatalogTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/catalog/FilePathCompletionWithCatalogTest.java new file mode 100644 index 000000000..7bd204957 --- /dev/null +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/catalog/FilePathCompletionWithCatalogTest.java @@ -0,0 +1,40 @@ +/******************************************************************************* +* Copyright (c) 2023 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.catalog; + +import org.eclipse.lemminx.commons.BadLocationException; +import org.eclipse.lemminx.extensions.filepath.participants.AbstractFilePathCompletionTest; +import org.eclipse.lsp4j.CompletionItem; +import org.junit.jupiter.api.Test; + +/** + * File path support completion test for XML catalog with + * + *
    + *
  • @uri
  • + *
+ * + * Test folders are in + * org.eclipse.lemminx/src/test/resources/filePathCompletion/ + */ +public class FilePathCompletionWithCatalogTest extends AbstractFilePathCompletionTest { + + @Test + public void uri() throws BadLocationException { + String xml = "\n" + + " \n" + + ""; + CompletionItem[] items = getCompletionItemList(1, 26, 26, "folderA", "folderB", "folderC", "NestedA", + "main.xsd", "main.dtd", "main.xml", "main.xsl"); + testCompletionFor(xml, 8, items); + } +} \ No newline at end of file diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/filepath/FilePathSettingsUtils.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/filepath/FilePathSettingsUtils.java new file mode 100644 index 000000000..36a43ed6d --- /dev/null +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/filepath/FilePathSettingsUtils.java @@ -0,0 +1,64 @@ +/******************************************************************************* +* Copyright (c) 2023 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.filepath; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.lemminx.extensions.filepath.settings.FilePathExpression; +import org.eclipse.lemminx.extensions.filepath.settings.FilePathMapping; +import org.eclipse.lemminx.extensions.filepath.settings.FilePathSupport; +import org.eclipse.lemminx.extensions.filepath.settings.FilePathSupportSettings; + +/** + * {@link FilePathSupportSettings} for tests. + * + */ +public class FilePathSettingsUtils { + + public static FilePathSupportSettings createFilePathsSettings() { + FilePathSupportSettings filePathsSettings = new FilePathSupportSettings(); + FilePathSupport support = new FilePathSupport(); + filePathsSettings.setFilePathSupport(support); + support.setMappings(createFilePathMappings()); + return filePathsSettings; + } + + private static List createFilePathMappings() { + List filePaths = new ArrayList<>(); + + FilePathMapping path = new FilePathMapping(); + path.setPattern("**/*.xml"); + filePaths.add(path); + /* + * { + * "xpath": "@path" + * }, + * { + * "xpath": "path/text()" + * }, + * { + * "xpath": "@paths", + * "separator": " " + * } + */ + FilePathExpression attrPath = new FilePathExpression("@path"); + FilePathExpression textPath = new FilePathExpression("path/text()"); + FilePathExpression multiAttrPath = new FilePathExpression("@paths").setSeparator(' '); + + path.setExpressions(Arrays.asList(attrPath, textPath, multiAttrPath)); + + return filePaths; + } + +} diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/filepath/participants/AbstractFilePathCompletionTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/filepath/participants/AbstractFilePathCompletionTest.java new file mode 100644 index 000000000..4cf2a6a82 --- /dev/null +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/filepath/participants/AbstractFilePathCompletionTest.java @@ -0,0 +1,76 @@ +/******************************************************************************* +* Copyright (c) 2019 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.filepath.participants; + +import static org.eclipse.lemminx.XMLAssert.c; +import static org.eclipse.lemminx.XMLAssert.te; + +import org.eclipse.lemminx.AbstractCacheBasedTest; +import org.eclipse.lemminx.XMLAssert; +import org.eclipse.lemminx.XMLAssert.SettingsSaveContext; +import org.eclipse.lemminx.commons.BadLocationException; +import org.eclipse.lemminx.extensions.filepath.FilePathSettingsUtils; +import org.eclipse.lemminx.services.XMLLanguageService; +import org.eclipse.lemminx.utils.FilesUtils; +import org.eclipse.lsp4j.CompletionItem; + +/** + * FilePathCompletionTest + * + * Test folders are in + * org.eclipse.lemminx/src/test/resources/filePathCompletion/ + */ +public abstract class AbstractFilePathCompletionTest extends AbstractCacheBasedTest { + + private static final String userDir = FilesUtils.encodePath(System.getProperty("user.dir")); // C:..\..\folderName + // || /bin/.../java + protected static final String userDirBackSlash = userDir.replace("/", "\\"); + protected static final String userDirForwardSlash = userDir.replace("\\", "/"); + + protected static void testCompletionFor(String xml, CompletionItem... expectedItems) throws BadLocationException { + testCompletionFor(xml, null, expectedItems); + } + + protected static void testCompletionFor(String xml, Integer expectedItemCount, CompletionItem... expectedItems) + throws BadLocationException { + String fileURI = getFileUri("main.xml"); + testCompletionFor(xml, fileURI, expectedItemCount, expectedItems); + } + + protected static String getFileUri(String fileName) { + return "file://" + userDirForwardSlash + "/src/test/resources/filePathCompletion/" + fileName; + } + + protected static void testCompletionFor(String xml, String fileURI, Integer expectedItemCount, + CompletionItem... expectedItems) + throws BadLocationException { + XMLAssert.testCompletionFor(new XMLLanguageService(), xml, null, ls -> { + ls.doSave(new SettingsSaveContext(FilePathSettingsUtils.createFilePathsSettings())); + + }, fileURI, expectedItemCount, true, expectedItems); + } + + protected static CompletionItem[] getCompletionItemList(int line, int startChar, int endChar, + String... fileOrFolderNames) { + int fOfSize = fileOrFolderNames.length; + CompletionItem[] items = new CompletionItem[fOfSize]; + + for (int i = 0; i < fOfSize; i++) { + String fOf = fileOrFolderNames[i]; + items[i] = c(fOf, te(line, startChar, line, endChar, fOf), fOf); + } + + return items; + + } + +} \ No newline at end of file diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionWithDOCTYPETest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionWithDOCTYPETest.java new file mode 100644 index 000000000..bd8fb3186 --- /dev/null +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionWithDOCTYPETest.java @@ -0,0 +1,103 @@ +/******************************************************************************* +* Copyright (c) 2023 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.filepath.participants; + +import static org.eclipse.lemminx.utils.platform.Platform.isWindows; + +import org.eclipse.lemminx.commons.BadLocationException; +import org.eclipse.lsp4j.CompletionItem; +import org.junit.jupiter.api.Test; + +/** + * File path support completion test with DOCTYPE. + * + * Test folders are in + * org.eclipse.lemminx/src/test/resources/filePathCompletion/ + */ +public class FilePathCompletionWithDOCTYPETest extends AbstractFilePathCompletionTest { + + @Test + public void empty() throws BadLocationException { + String xml = ""; + CompletionItem[] items = getCompletionItemList(0, 22, 22, "folderA", "folderB", "folderC", "NestedA", + "main.dtd"); + testCompletionFor(xml, 5, items); + } + + @Test + public void dotSlash() throws BadLocationException { + String xml = ""; + CompletionItem[] items = getCompletionItemList(0, 24, 24, "folderA", "folderB", "folderC", "NestedA", + "main.dtd"); + testCompletionFor(xml, 5, items); + } + + @Test + public void dotSlashFollowingBySlash() throws BadLocationException { + String xml = ""; + CompletionItem[] items = getCompletionItemList(0, 24, 25, "folderA", "folderB", "folderC", "NestedA", + "main.dtd"); + testCompletionFor(xml, 5, items); + } + + @Test + public void backSlash() throws BadLocationException { + if (!isWindows) { + return; + } + String xml = ""; + CompletionItem[] items = getCompletionItemList(0, 24, 24, "folderA", "folderB", "folderC", "NestedA", + "main.dtd"); + testCompletionFor(xml, 5, items); + } + + @Test + public void afterFolderA() throws BadLocationException { + String xml = ""; + CompletionItem[] items = getCompletionItemList(0, 32, 32, "dtdA1.dtd", "dtdA2.dtd"); + testCompletionFor(xml, 2, items); + } + + @Test + public void afterFolderABackSlash() throws BadLocationException { + if (!isWindows) { + return; + } + String xml = ""; + CompletionItem[] items = getCompletionItemList(0, 32, 32, "dtdA1.dtd", "dtdA2.dtd"); + testCompletionFor(xml, 2, items); + } + + @Test + public void afterFolderB() throws BadLocationException { + String xml = ""; + CompletionItem[] items = getCompletionItemList(0, 32, 32, "dtdB1.dtd"); + testCompletionFor(xml, 1, items); + } + + @Test + public void afterFolderBBackSlash() throws BadLocationException { + if (!isWindows) { + return; + } + String xml = ""; + CompletionItem[] items = getCompletionItemList(0, 32, 32, "dtdB1.dtd"); + testCompletionFor(xml, 1, items); + } + + @Test + public void testFilePathNoCompletionMissingSystemId() throws BadLocationException { + String xml = ""; + testCompletionFor(xml, 0); + } + +} \ No newline at end of file diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionWithNoNamespaceSchemaLocationTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionWithNoNamespaceSchemaLocationTest.java new file mode 100644 index 000000000..378832a75 --- /dev/null +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionWithNoNamespaceSchemaLocationTest.java @@ -0,0 +1,122 @@ +/******************************************************************************* +* Copyright (c) 2023 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.filepath.participants; + +import static org.eclipse.lemminx.utils.platform.Platform.isWindows; + +import org.eclipse.lemminx.commons.BadLocationException; +import org.eclipse.lsp4j.CompletionItem; +import org.junit.jupiter.api.Test; + +/** + * File path support completion test with xsi:noNamespaceSchemaLocation. + * + * Test folders are in + * org.eclipse.lemminx/src/test/resources/filePathCompletion/ + */ +public class FilePathCompletionWithNoNamespaceSchemaLocationTest extends AbstractFilePathCompletionTest { + + @Test + public void empty() throws BadLocationException { + String xml = "\n" + + ""; + CompletionItem[] items = getCompletionItemList(2, 32, 32, "folderA", "folderB", "folderC", "NestedA", + "main.xsd"); + testCompletionFor(xml, 5, items); + } + + @Test + public void dotSlash() throws BadLocationException { + String xml = "\n" + + ""; + CompletionItem[] items = getCompletionItemList(2, 34, 34, "folderA", "folderB", "folderC", "NestedA", + "main.xsd"); + testCompletionFor(xml, 5, items); + + } + + @Test + public void dotSlashFollowingBySlash() throws BadLocationException { + String xml = "\n" + + ""; + CompletionItem[] items = getCompletionItemList(2, 34, 35, "folderA", "folderB", "folderC", "NestedA", + "main.xsd"); + testCompletionFor(xml, 5, items); + } + + @Test + public void backSlash() throws BadLocationException { + if (!isWindows) { + return; + } + String xml = "\n" + + ""; + CompletionItem[] items = getCompletionItemList(2, 34, 34, "folderA", "folderB", "folderC", "NestedA", + "main.xsd"); + testCompletionFor(xml, 5, items); + } + + @Test + public void afterFolderA() throws BadLocationException { + String xml = "\n" + + ""; + CompletionItem[] items = getCompletionItemList(2, 42, 42, "xsdA1.xsd", "xsdA2.xsd"); + testCompletionFor(xml, 2, items); + } + + @Test + public void afterFolderABackSlash() throws BadLocationException { + if (!isWindows) { + return; + } + String xml = "\n" + + ""; + CompletionItem[] items = getCompletionItemList(2, 42, 42, "xsdA1.xsd", "xsdA2.xsd"); + testCompletionFor(xml, 2, items); + } + + @Test + public void afterFolderB() throws BadLocationException { + String xml = "\n" + + ""; + CompletionItem[] items = getCompletionItemList(2, 42, 42, "xsdB1.xsd"); + testCompletionFor(xml, 1, items); + } + + @Test + public void afterFolderBBackSlash() throws BadLocationException { + if (!isWindows) { + return; + } + String xml = "\n" + + ""; + CompletionItem[] items = getCompletionItemList(2, 42, 42, "xsdB1.xsd"); + testCompletionFor(xml, 1, items); + } + +} \ No newline at end of file diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionWithSchemaLocationTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionWithSchemaLocationTest.java new file mode 100644 index 000000000..a337d9297 --- /dev/null +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionWithSchemaLocationTest.java @@ -0,0 +1,38 @@ +/******************************************************************************* +* Copyright (c) 2023 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.filepath.participants; + +import org.eclipse.lemminx.commons.BadLocationException; +import org.eclipse.lsp4j.CompletionItem; +import org.junit.jupiter.api.Test; + +/** + * File path support completion test with xsi:schemaLocation. + * + * Test folders are in + * org.eclipse.lemminx/src/test/resources/filePathCompletion/ + */ +public class FilePathCompletionWithSchemaLocationTest extends AbstractFilePathCompletionTest { + + @Test + public void empty() throws BadLocationException { + String xml = "\n" + + ""; + CompletionItem[] items = getCompletionItemList(3, 37, 37, "folderA", "folderB", "folderC", "NestedA", + "main.xsd"); + testCompletionFor(xml, 5, items); + } + +} \ No newline at end of file diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/general/FilePathCompletionTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionWithSettingsTest.java similarity index 52% rename from org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/general/FilePathCompletionTest.java rename to org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionWithSettingsTest.java index 749586bc7..c1b0e070b 100644 --- a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/general/FilePathCompletionTest.java +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionWithSettingsTest.java @@ -9,71 +9,85 @@ * Contributors: * Red Hat Inc. - initial API and implementation *******************************************************************************/ -package org.eclipse.lemminx.extensions.general; +package org.eclipse.lemminx.extensions.filepath.participants; -import static org.eclipse.lemminx.XMLAssert.c; -import static org.eclipse.lemminx.XMLAssert.te; import static org.eclipse.lemminx.utils.platform.Platform.isWindows; -import org.eclipse.lemminx.AbstractCacheBasedTest; -import org.eclipse.lemminx.XMLAssert; import org.eclipse.lemminx.commons.BadLocationException; import org.eclipse.lemminx.utils.FilesUtils; -import org.eclipse.lemminx.utils.platform.Platform; import org.eclipse.lsp4j.CompletionItem; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; /** - * FilePathCompletionTest + * File path support completion test with user settings. * * Test folders are in * org.eclipse.lemminx/src/test/resources/filePathCompletion/ */ -public class FilePathCompletionTest extends AbstractCacheBasedTest { +public class FilePathCompletionWithSettingsTest extends AbstractFilePathCompletionTest { - private static final String userDir = FilesUtils.encodePath(System.getProperty("user.dir")); // C:..\..\folderName - // || /bin/.../java - private static final String userDirBackSlash = userDir.replace("/", "\\"); - private static final String userDirForwardSlash = userDir.replace("\\", "/"); + @Test + public void empty() throws BadLocationException { + String xml = "
"; + CompletionItem[] items = getCompletionItemList(0, 9, 9, "folderA", "folderB", "folderC", "NestedA", "main.xml", + "main.xsd", "main.xsl", "main.dtd"); + testCompletionFor(xml, 8, items); + } @Test public void testFilePathCompletion() throws BadLocationException { String xml = ""; - CompletionItem[] items = getCompletionItemList("/", 0, 10, 11, "folderA", "folderB", "NestedA"); - testCompletionFor(xml, items); + CompletionItem[] items = getCompletionItemList(0, 11, 11, "folderA", "folderB", "folderC", "NestedA", + "main.xml", + "main.xsd", "main.xsl", "main.dtd"); + testCompletionFor(xml, 8, items); } @Test public void testFilePathCompletionBackSlash() throws BadLocationException { + if (!isWindows) { + return; + } String xml = ""; - CompletionItem[] items = getCompletionItemList("\\", 0, 10, 11, "folderA", "folderB", "NestedA"); - testCompletionFor(xml, items); + CompletionItem[] items = getCompletionItemList(0, 11, 11, "folderA", "folderB", "folderC", "NestedA", + "main.xml", + "main.xsd", "main.xsl", "main.dtd"); + testCompletionFor(xml, 8, items); } @Test public void testFilePathCompletionFolderA() throws BadLocationException { String xml = ""; - CompletionItem[] items = getCompletionItemList("/", 0, 18, 19, "xsdA1.xsd", "xsdA2.xsd"); + CompletionItem[] items = getCompletionItemList(0, 19, 19, "xsdA1.xsd", "xsdA2.xsd"); testCompletionFor(xml, items); } @Test public void testFilePathCompletionFolderABackSlash() throws BadLocationException { + if (!isWindows) { + return; + } String xml = ""; - CompletionItem[] items = getCompletionItemList("\\", 0, 18, 19, "xsdA1.xsd", "xsdA2.xsd"); + CompletionItem[] items = getCompletionItemList(0, 19, 19, "xsdA1.xsd", "xsdA2.xsd"); testCompletionFor(xml, items); } @Test public void testFilePathCompletionFolderB() throws BadLocationException { String xml = ""; - testCompletionFor(xml, 0); + CompletionItem[] items = getCompletionItemList(0, 17, 17, "xsdB1.xsd", "xmlB1.xml", "dtdB1.dtd"); + testCompletionFor(xml, 3, items); } @Test public void testFilePathCompletionFolderBBackSlash() throws BadLocationException { + if (!isWindows) { + return; + } String xml = ""; - testCompletionFor(xml, 0); + CompletionItem[] items = getCompletionItemList(0, 17, 17, "xsdB1.xsd", "xmlB1.xml", "dtdB1.dtd"); + testCompletionFor(xml, 3, items); } @Test @@ -81,9 +95,9 @@ public void testFilePathCompletionFolderBAbsolutePath() throws BadLocationExcept String filePath = userDirForwardSlash + "/src/test/resources/filePathCompletion/folderB/"; // C:/.../src/test... int filePathLength = filePath.length(); String xml = ""; - CompletionItem[] items = getCompletionItemList("/", 0, 9 + filePathLength - 1, 9 + filePathLength, "xsdB1.xsd", - "xmlB1.xml"); - testCompletionFor(xml, 2, items); + CompletionItem[] items = getCompletionItemList(0, 9 + filePathLength, 9 + filePathLength, "xsdB1.xsd", + "xmlB1.xml", "dtdB1.dtd"); + testCompletionFor(xml, 3, items); } @Test @@ -94,9 +108,9 @@ public void testFilePathCompletionFolderBAbsolutePathBackSlash() throws BadLocat String filePath = userDirBackSlash + "\\src\\test\\resources\\filePathCompletion\\folderB\\"; // C:\...\src\test... int filePathLength = filePath.length(); String xml = ""; - CompletionItem[] items = getCompletionItemList("\\", 0, 9 + filePathLength - 1, 9 + filePathLength, "xsdB1.xsd", - "xmlB1.xml"); - testCompletionFor(xml, 2, items); + CompletionItem[] items = getCompletionItemList(0, 9 + filePathLength, 9 + filePathLength, "xsdB1.xsd", + "xmlB1.xml", "dtdB1.dtd"); + testCompletionFor(xml, 3, items); } @Test @@ -105,36 +119,42 @@ public void testFilePathCompletionFolderBAbsolutePathWithFileScheme() throws Bad + "/src/test/resources/filePathCompletion/folderB/"; int filePathLength = filePath.length(); String xml = ""; - CompletionItem[] items = getCompletionItemList("/", 0, 9 + filePathLength - 1, 9 + filePathLength, "xsdB1.xsd", - "xmlB1.xml"); - testCompletionFor(xml, 2, items); + CompletionItem[] items = getCompletionItemList(0, 9 + filePathLength, 9 + filePathLength, "xsdB1.xsd", + "xmlB1.xml", "dtdB1.dtd"); + testCompletionFor(xml, 3, items); } @Test public void testFilePathCompletionNestedA() throws BadLocationException { String xml = ""; - CompletionItem[] items = getCompletionItemList("/", 0, 18, 19, "NestedB"); + CompletionItem[] items = getCompletionItemList(0, 19, 19, "NestedB"); testCompletionFor(xml, 1, items); } @Test public void testFilePathCompletionNestedABackSlash() throws BadLocationException { + if (!isWindows) { + return; + } String xml = ""; - CompletionItem[] items = getCompletionItemList("\\", 0, 18, 19, "NestedB"); + CompletionItem[] items = getCompletionItemList(0, 19, 19, "NestedB"); testCompletionFor(xml, 1, items); } @Test public void testFilePathCompletionNestedBIncomplete() throws BadLocationException { String xml = ""; - CompletionItem[] items = getCompletionItemList("/", 0, 26, 30, "nestedXSD.xsd"); + CompletionItem[] items = getCompletionItemList(0, 27, 30, "nestedXSD.xsd"); testCompletionFor(xml, 1, items); } @Test public void testFilePathCompletionNestedBIncompleteBackSlash() throws BadLocationException { + if (!isWindows) { + return; + } String xml = ""; - CompletionItem[] items = getCompletionItemList("\\", 0, 26, 30, "nestedXSD.xsd"); + CompletionItem[] items = getCompletionItemList(0, 27, 30, "nestedXSD.xsd"); testCompletionFor(xml, 1, items); } @@ -150,23 +170,6 @@ public void testFilePathCompletionExtraTextInValueBackSlash() throws BadLocation testCompletionFor(xml, 0); } - @Test - public void testFilePathCompletionExtraTextInValueAbsolute() throws BadLocationException { - String filePath = userDirForwardSlash + "/src/test/resources/filePathCompletion/NestedA/NestedB/"; - int filePathLength = filePath.length(); - String xml = ""; - CompletionItem[] items = getCompletionItemList("/", 0, 29 + filePathLength - 1, 29 + filePathLength, - "nestedXSD.xsd"); - testCompletionFor(xml, 1, items); - } - - @Test - public void testFilePathCompletionExtraTextInValueAbsoluteBackSlash() throws BadLocationException { - String filePath = userDirBackSlash + "\\src\\test\\resources\\filePathCompletion\\NestedA\\NestedB\\"; - String xml = ""; - testCompletionFor(xml, Platform.isWindows ? 1 : 0); - } - @Test public void testFilePathCompletionBadFolder() throws BadLocationException { String xml = ""; @@ -182,29 +185,35 @@ public void testFilePathCompletionBadFolderBackSlash() throws BadLocationExcepti @Test public void testFilePathCompletionStartWithDotDot() throws BadLocationException { String xml = ""; - CompletionItem[] items = getCompletionItemList("/", 0, 38, 39, "xsdA1.xsd", "xsdA2.xsd"); - testCompletionFor(xml, 2, items); + CompletionItem[] items = getCompletionItemList(0, 39, 39, "xsdA1.xsd", "xsdA2.xsd", "dtdA1.dtd", "dtdA2.dtd"); + testCompletionFor(xml, 4, items); } @Test public void testFilePathCompletionStartWithDotDotBackSlash() throws BadLocationException { + if (!isWindows) { + return; + } String xml = ""; - CompletionItem[] items = getCompletionItemList("\\", 0, 38, 39, "xsdA1.xsd", "xsdA2.xsd"); - testCompletionFor(xml, 2, items); + CompletionItem[] items = getCompletionItemList(0, 39, 39, "xsdA1.xsd", "xsdA2.xsd", "dtdA1.dtd", "dtdA2.dtd"); + testCompletionFor(xml, 4, items); } @Test public void testFilePathCompletionStartWithDot() throws BadLocationException { String xml = ""; - CompletionItem[] items = getCompletionItemList("/", 0, 18, 19, "xsdA1.xsd", "xsdA2.xsd"); - testCompletionFor(xml, 2, items); + CompletionItem[] items = getCompletionItemList(0, 19, 19, "xsdA1.xsd", "xsdA2.xsd", "dtdA1.dtd", "dtdA2.dtd"); + testCompletionFor(xml, 4, items); } @Test public void testFilePathCompletionStartWithDotBackSlash() throws BadLocationException { + if (!isWindows) { + return; + } String xml = ""; - CompletionItem[] items = getCompletionItemList("\\", 0, 18, 19, "xsdA1.xsd", "xsdA2.xsd"); - testCompletionFor(xml, 2, items); + CompletionItem[] items = getCompletionItemList(0, 19, 19, "xsdA1.xsd", "xsdA2.xsd", "dtdA1.dtd", "dtdA2.dtd"); + testCompletionFor(xml, 4, items); } @Test @@ -219,82 +228,22 @@ public void testFilePathCompletionEndsWithFileAndBackSlash() throws BadLocationE testCompletionFor(xml, 0); } - @Test - public void testFilePathCompletionNotValue() throws BadLocationException { - String xml = ""; - testCompletionFor(xml, 0); - } - - @Test - public void testFilePathCompletionDTD() throws BadLocationException { - String xml = ""; - CompletionItem[] items = getCompletionItemList("/", 0, 23, 24, "folderA", "folderB", "NestedA"); - testCompletionFor(xml, items); - } - - @Test - public void testFilePathCompletionDTDBackSlash() throws BadLocationException { - - String xml = ""; - CompletionItem[] items = getCompletionItemList("\\", 0, 23, 24, "folderA", "folderB", "NestedA"); - testCompletionFor(xml, items); - } - - @Test - public void testFilePathCompletionDTDFolderA() throws BadLocationException { - String xml = ""; - CompletionItem[] items = getCompletionItemList("/", 0, 31, 32, "xsdA1.xsd", "xsdA2.xsd"); - testCompletionFor(xml, items); - } - - @Test - public void testFilePathCompletionDTDFolderABackSlash() throws BadLocationException { - String xml = ""; - CompletionItem[] items = getCompletionItemList("\\", 0, 31, 32, "xsdA1.xsd", "xsdA2.xsd"); - testCompletionFor(xml, items); - } - - @Test - public void testFilePathCompletionDTDFolderB() throws BadLocationException { - String xml = ""; - CompletionItem[] items = getCompletionItemList("/", 0, 31, 32, "xsdB1.xsd", "xmlB1.xml"); - testCompletionFor(xml, 2, items); - } - - @Test - public void testFilePathCompletionDTDFolderBBackSlash() throws BadLocationException { - String xml = ""; - CompletionItem[] items = getCompletionItemList("\\", 0, 31, 32, "xsdB1.xsd", "xmlB1.xml"); - testCompletionFor(xml, 2, items); - } - - @Test - public void testFilePathCompletionForEmptyDoctype() throws BadLocationException { - String xml = ""; - testCompletionFor(xml, 11); - } - - @Test - public void testFilePathNoCompletionMissingSystemId() throws BadLocationException { - String xml = ""; - testCompletionFor(xml, 0); - } - @Test public void testFilePathCompletionWithSpacesFolder() throws BadLocationException { String xml = ""; - CompletionItem[] items = getCompletionItemList("/", 0, 18, 19, "a@b", "with%20spaces"); + CompletionItem[] items = getCompletionItemList(0, 19, 19, "a@b", "with%20spaces"); testCompletionFor(xml, 2, items); } @Test public void testFilePathCompletionInsideSpecialChars() throws BadLocationException { String xml = ""; - CompletionItem[] items = getCompletionItemList("/", 0, 11, 12, "a@b", "with%20spaces"); String fileURI = "file://" + userDirForwardSlash + "/src/test/resources/filePathCompletion/folderC/a@b/foo.xml"; - XMLAssert.testCompletionFor(xml, null, fileURI, 2, items); + CompletionItem[] items = getCompletionItemList(0, 12, 12, "a@b", "with%20spaces"); + testCompletionFor(xml, fileURI, 2, items); } + @Disabled @Test public void testFilePathCompletionWithBrokenAbsoluteWindowsPath() throws BadLocationException { String xml = ""; @@ -307,29 +256,48 @@ public void testFilePathCompletionWithBrokenAbsoluteWindowsPath() throws BadLoca testCompletionFor(xml, 0); } - private static void testCompletionFor(String xml, CompletionItem... expectedItems) throws BadLocationException { - testCompletionFor(xml, null, expectedItems); - } + // Test with multiple file path - private static void testCompletionFor(String xml, Integer expectedItemCount, CompletionItem... expectedItems) - throws BadLocationException { - String fileURI = "file://" + userDirForwardSlash + "/src/test/resources/filePathCompletion/main.xml"; - XMLAssert.testCompletionFor(xml, null, fileURI, expectedItemCount, expectedItems); + @Test + public void testFilePathCompletionExtraTextInValueAbsolute() throws BadLocationException { + String filePath = userDirForwardSlash + "/src/test/resources/filePathCompletion/NestedA/NestedB/"; + int filePathLength = filePath.length(); + String xml = ""; + CompletionItem[] items = getCompletionItemList(0, 30 + filePathLength, 30 + filePathLength, "nestedXSD.xsd"); + testCompletionFor(xml, 1, items); } - private static CompletionItem[] getCompletionItemList(String slash, int line, int startChar, int endChar, - String... fileOrFolderNames) { - String s = slash; - int fOfSize = fileOrFolderNames.length; - CompletionItem[] items = new CompletionItem[fOfSize]; - - for (int i = 0; i < fOfSize; i++) { - String fOf = s + fileOrFolderNames[i]; - items[i] = c(fOf, te(line, startChar, line, endChar, fOf), fOf); + @Test + public void testFilePathCompletionExtraTextInValueAbsoluteBackSlash() throws BadLocationException { + if (!isWindows) { + return; } + String filePath = userDirBackSlash + "\\src\\test\\resources\\filePathCompletion\\NestedA\\NestedB\\"; + int filePathLength = filePath.length(); - return items; + String xml = ""; + testCompletionFor(xml, 0); + xml = ""; + CompletionItem[] items = getCompletionItemList(0, 30 + filePathLength, 30 + filePathLength, "nestedXSD.xsd"); + testCompletionFor(xml, 1, items); + } + + @Test + public void beforeFirstPath() throws BadLocationException { + String xml = ""; + CompletionItem[] items = getCompletionItemList(0, 10, 10, "folderA", "folderB", "folderC", "NestedA", "main.xml", + "main.xsd", "main.xsl", "main.dtd"); + testCompletionFor(xml, 8, items); + } + + + @Test + public void afterFirstPath() throws BadLocationException { + String xml = ""; + CompletionItem[] items = getCompletionItemList(0, 18, 18, "folderA", "folderB", "folderC", "NestedA", "main.xml", + "main.xsd", "main.xsl", "main.dtd"); + testCompletionFor(xml, 8, items); } } \ No newline at end of file diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/relaxng/grammar/rng/FilePathCompletionWithRNGTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/relaxng/grammar/rng/FilePathCompletionWithRNGTest.java new file mode 100644 index 000000000..335fddd98 --- /dev/null +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/relaxng/grammar/rng/FilePathCompletionWithRNGTest.java @@ -0,0 +1,53 @@ +/******************************************************************************* +* Copyright (c) 2023 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.relaxng.grammar.rng; + +import org.eclipse.lemminx.commons.BadLocationException; +import org.eclipse.lemminx.extensions.filepath.participants.AbstractFilePathCompletionTest; +import org.eclipse.lsp4j.CompletionItem; +import org.junit.jupiter.api.Test; + +/** + * File path support completion test with + * + *
    + *
  • include/@href
  • + *
  • externalRef/@href
  • + *
+ * + * Test folders are in + * org.eclipse.lemminx/src/test/resources/filePathCompletion/ + */ +public class FilePathCompletionWithRNGTest extends AbstractFilePathCompletionTest { + + @Test + public void includeHref() throws BadLocationException { + String xml = "\n" + + " \n" + + ""; + CompletionItem[] items = getCompletionItemList(2, 16, 16, "folderA", "folderB", "folderC", "NestedA", + "main.xsd", "main.dtd", "main.xml", "main.xsl"); + testCompletionFor(xml, 8, items); + } + + @Test + public void externalRefHref() throws BadLocationException { + String xml = "\n" + + " \n" + + ""; + CompletionItem[] items = getCompletionItemList(2, 20, 20, "folderA", "folderB", "folderC", "NestedA", + "main.xsd", "main.dtd", "main.xml", "main.xsl"); + testCompletionFor(xml, 8, items); + } +} \ No newline at end of file diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/xsd/FilePathCompletionWithXSDTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/xsd/FilePathCompletionWithXSDTest.java new file mode 100644 index 000000000..f25a5cce5 --- /dev/null +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/xsd/FilePathCompletionWithXSDTest.java @@ -0,0 +1,51 @@ +/******************************************************************************* +* Copyright (c) 2023 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.xsd; + +import org.eclipse.lemminx.commons.BadLocationException; +import org.eclipse.lemminx.extensions.filepath.participants.AbstractFilePathCompletionTest; +import org.eclipse.lsp4j.CompletionItem; +import org.junit.jupiter.api.Test; + +/** + * File path support completion test with + * + *
    + *
  • include/@schemaLocation
  • + *
  • import/@schemaLocation
  • + *
+ * + * Test folders are in + * org.eclipse.lemminx/src/test/resources/filePathCompletion/ + */ +public class FilePathCompletionWithXSDTest extends AbstractFilePathCompletionTest { + + @Test + public void includeSchemaLocation() throws BadLocationException { + String xml = "\n" + + " \n" + + ""; + CompletionItem[] items = getCompletionItemList(1, 29, 29, "folderA", "folderB", "folderC", "NestedA", + "main.xsd"); + testCompletionFor(xml, 5, items); + } + + @Test + public void importSchemaLocation() throws BadLocationException { + String xml = "\n" + + " \n" + + ""; + CompletionItem[] items = getCompletionItemList(1, 28, 28, "folderA", "folderB", "folderC", "NestedA", + "main.xsd"); + testCompletionFor(xml, 5, items); + } +} \ No newline at end of file diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/xsl/FilePathCompletionWithXSLTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/xsl/FilePathCompletionWithXSLTest.java new file mode 100644 index 000000000..ab8600661 --- /dev/null +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/xsl/FilePathCompletionWithXSLTest.java @@ -0,0 +1,51 @@ +/******************************************************************************* +* Copyright (c) 2023 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.xsl; + +import org.eclipse.lemminx.commons.BadLocationException; +import org.eclipse.lemminx.extensions.filepath.participants.AbstractFilePathCompletionTest; +import org.eclipse.lsp4j.CompletionItem; +import org.junit.jupiter.api.Test; + +/** + * File path support completion test for XSL with + * + *
    + *
  • include/@href
  • + *
  • import/@href
  • + *
+ * + * Test folders are in + * org.eclipse.lemminx/src/test/resources/filePathCompletion/ + */ +public class FilePathCompletionWithXSLTest extends AbstractFilePathCompletionTest { + + @Test + public void includeHref() throws BadLocationException { + String xml = "\n" + + " \n" + + ""; + CompletionItem[] items = getCompletionItemList(1, 20, 20, "folderA", "folderB", "folderC", "NestedA", + "main.xsl"); + testCompletionFor(xml, 5, items); + } + + @Test + public void importHref() throws BadLocationException { + String xml = "\n" + + " \n" + + ""; + CompletionItem[] items = getCompletionItemList(1, 19, 19, "folderA", "folderB", "folderC", "NestedA", + "main.xsl"); + testCompletionFor(xml, 5, items); + } +} \ No newline at end of file diff --git a/org.eclipse.lemminx/src/test/resources/filePathCompletion/folderA/dtdA1.dtd b/org.eclipse.lemminx/src/test/resources/filePathCompletion/folderA/dtdA1.dtd new file mode 100644 index 000000000..e69de29bb diff --git a/org.eclipse.lemminx/src/test/resources/filePathCompletion/folderA/dtdA2.dtd b/org.eclipse.lemminx/src/test/resources/filePathCompletion/folderA/dtdA2.dtd new file mode 100644 index 000000000..e69de29bb diff --git a/org.eclipse.lemminx/src/test/resources/filePathCompletion/folderB/dtdB1.dtd b/org.eclipse.lemminx/src/test/resources/filePathCompletion/folderB/dtdB1.dtd new file mode 100644 index 000000000..e69de29bb diff --git a/org.eclipse.lemminx/src/test/resources/filePathCompletion/main.dtd b/org.eclipse.lemminx/src/test/resources/filePathCompletion/main.dtd new file mode 100644 index 000000000..e69de29bb diff --git a/org.eclipse.lemminx/src/test/resources/filePathCompletion/main.xsd b/org.eclipse.lemminx/src/test/resources/filePathCompletion/main.xsd new file mode 100644 index 000000000..e69de29bb diff --git a/org.eclipse.lemminx/src/test/resources/filePathCompletion/main.xsl b/org.eclipse.lemminx/src/test/resources/filePathCompletion/main.xsl new file mode 100644 index 000000000..e69de29bb