Skip to content

Commit

Permalink
Outline should display referenced DTD / XSD from the current XML
Browse files Browse the repository at this point in the history
Fixes #892

Signed-off-by: azerr <azerr@redhat.com>
  • Loading branch information
azerr authored and datho7561 committed Oct 1, 2020
1 parent dbf43f3 commit 7fe05f1
Show file tree
Hide file tree
Showing 33 changed files with 1,330 additions and 197 deletions.
1 change: 1 addition & 0 deletions docs/LemMinX-Extensions.md
Expand Up @@ -99,6 +99,7 @@ The [LemMinx Extensions API](https://github.com/eclipse/lemminx/tree/master/org.
- Type Definitions with [ITypeDefinitionParticipant](https://github.com/eclipse/lemminx/blob/master/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/ITypeDefinitionParticipant.java)
- CodeLens with [ICodeLensParticipant](https://github.com/eclipse/lemminx/blob/master/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/codelens/ICodeLensParticipant.java)
- Formatter with [IFormatterParticipant](https://github.com/eclipse/lemminx/blob/master/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/format/IFormatterParticipant.java)
- Symbols with [ISymbolsProviderParticipant](https://github.com/eclipse/lemminx/blob/master/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/ISymbolsProviderParticipant.java)

## Adding custom settings for your extension

Expand Down
Expand Up @@ -241,17 +241,17 @@ public CompletableFuture<List<Either<SymbolInformation, DocumentSymbol>>> docume
return e;
}) //
.collect(Collectors.toList());
} else {
SymbolInformationResult result = getXMLLanguageService().findSymbolInformations(xmlDocument,
symbolSettings, cancelChecker);
resultLimitExceeded = result.isResultLimitExceeded();
symbols = result.stream() //
.map(s -> {
Either<SymbolInformation, DocumentSymbol> e = Either.forLeft(s);
return e;
}) //
.collect(Collectors.toList());
}
SymbolInformationResult result = getXMLLanguageService().findSymbolInformations(xmlDocument, symbolSettings,
cancelChecker);
resultLimitExceeded = result.isResultLimitExceeded();
symbols = result.stream() //
.map(s -> {
Either<SymbolInformation, DocumentSymbol> e = Either.forLeft(s);
return e;
}) //
.collect(Collectors.toList());

if (resultLimitExceeded) {
// send warning
getLimitExceededWarner().onResultLimitExceeded(xmlDocument.getTextDocument().getUri(),
Expand Down Expand Up @@ -562,8 +562,7 @@ private static <R, M> CompletableFuture<R> computeModelAsync(CompletableFuture<M

public LimitExceededWarner getLimitExceededWarner() {
if (this.limitExceededWarner == null) {
this.limitExceededWarner =
new LimitExceededWarner(this.xmlLanguageServer);
this.limitExceededWarner = new LimitExceededWarner(this.xmlLanguageServer);
}
return this.limitExceededWarner;
}
Expand Down
Expand Up @@ -62,9 +62,8 @@ public SchemaLocation(DOMAttr attr) {
* @return The associated location hint, as a string, or null
* if the namespace was not referred to in xsi:schemaLocation
*/
public String getLocationHint(String namespaceURI) {
SchemaLocationHint locHint = schemaLocationValuePairs.get(namespaceURI);
return locHint == null ? null : locHint.getHint();
public SchemaLocationHint getLocationHint(String namespaceURI) {
return schemaLocationValuePairs.get(namespaceURI);
}

public DOMAttr getAttr() {
Expand Down
Expand Up @@ -37,6 +37,11 @@ public class XMLCatalogURIResolverExtension implements URIResolverExtension {

private final XMLExtensionsRegistry extensionsRegistry;

@Override
public String getName() {
return "embedded catalog.xsd";
}

public XMLCatalogURIResolverExtension(XMLExtensionsRegistry extensionsRegistry) {
this.extensionsRegistry = extensionsRegistry;
}
Expand Down
Expand Up @@ -18,6 +18,7 @@
import org.eclipse.lemminx.extensions.contentmodel.participants.ContentModelCompletionParticipant;
import org.eclipse.lemminx.extensions.contentmodel.participants.ContentModelDocumentLinkParticipant;
import org.eclipse.lemminx.extensions.contentmodel.participants.ContentModelHoverParticipant;
import org.eclipse.lemminx.extensions.contentmodel.participants.ContentModelSymbolsProviderParticipant;
import org.eclipse.lemminx.extensions.contentmodel.participants.ContentModelTypeDefinitionParticipant;
import org.eclipse.lemminx.extensions.contentmodel.participants.diagnostics.ContentModelDiagnosticsParticipant;
import org.eclipse.lemminx.extensions.contentmodel.settings.ContentModelSettings;
Expand Down Expand Up @@ -57,6 +58,8 @@ public class ContentModelPlugin implements IXMLExtension {

private final ITypeDefinitionParticipant typeDefinitionParticipant;

private ContentModelSymbolsProviderParticipant symbolsProviderParticipant;

ContentModelManager contentModelManager;

private ContentModelSettings cmSettings;
Expand Down Expand Up @@ -133,6 +136,9 @@ private void updateSettings(ContentModelSettings settings, ISaveContext context)
if (useCache != null) {
contentModelManager.setUseCache(useCache);
}
// Update symbols
boolean showReferencedGrammars = settings.isShowReferencedGrammars();
symbolsProviderParticipant.setEnabled(showReferencedGrammars);
}

@Override
Expand All @@ -150,6 +156,8 @@ public void start(InitializeParams params, XMLExtensionsRegistry registry) {
registry.registerCodeActionParticipant(codeActionParticipant);
registry.registerDocumentLinkParticipant(documentLinkParticipant);
registry.registerTypeDefinitionParticipant(typeDefinitionParticipant);
symbolsProviderParticipant = new ContentModelSymbolsProviderParticipant(contentModelManager);
registry.registerSymbolsProviderParticipant(symbolsProviderParticipant);
}

@Override
Expand All @@ -160,14 +168,15 @@ public void stop(XMLExtensionsRegistry registry) {
registry.unregisterCodeActionParticipant(codeActionParticipant);
registry.unregisterDocumentLinkParticipant(documentLinkParticipant);
registry.unregisterTypeDefinitionParticipant(typeDefinitionParticipant);
registry.unregisterSymbolsProviderParticipant(symbolsProviderParticipant);
}

public ContentModelSettings getContentModelSettings() {
return cmSettings;
}

public ContentModelManager getContentModelManager() {
return contentModelManager;
}

}
Expand Up @@ -12,13 +12,16 @@
*/
package org.eclipse.lemminx.extensions.contentmodel.model;

import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.xerces.xni.grammars.XMLGrammarPool;
import org.eclipse.lemminx.dom.DOMDocument;
Expand All @@ -30,6 +33,8 @@
import org.eclipse.lemminx.extensions.contentmodel.uriresolver.XMLCatalogResolverExtension;
import org.eclipse.lemminx.extensions.contentmodel.uriresolver.XMLFileAssociationResolverExtension;
import org.eclipse.lemminx.uriresolver.CacheResourceDownloadingException;
import org.eclipse.lemminx.uriresolver.CacheResourcesManager;
import org.eclipse.lemminx.uriresolver.ResolvedURIInfo;
import org.eclipse.lemminx.uriresolver.URIResolverExtensionManager;
import org.eclipse.lemminx.utils.StringUtils;
import org.eclipse.lemminx.utils.URIUtils;
Expand Down Expand Up @@ -187,6 +192,83 @@ public boolean dependsOnGrammar(DOMDocument document, String grammarURI) {
return grammarURI.equals(key);
}

/**
* Returns informations about all referenced grammar (XSD, DTD) from the given
* DOM document.
*
* <p>
* In other words, it gives information about
*
* <ul>
* <li>which XSD, DTD files are bound to the DOM document.</li>
* <li>which binding strategies are used (catalog, file association,
* xsi:schemaLocation, xsi:noNamespaceSchemaLocation, DOCTYPE).</li>
* <li>the cache file path (for remote grammar with http://)</li>
*
* </p>
*
* @param document the DOM document.
*
* @return informations about all referenced grammar (XSD, DTD) from the given
* DOM document.
*/
public Set<ReferencedGrammarInfo> getReferencedGrammarInfos(DOMDocument document) {
Set<ReferencedGrammarInfo> referencedGrammarInfos = new HashSet<>();
for (ContentModelProvider modelProvider : modelProviders) {
if (modelProvider.adaptFor(document, false)) {
Collection<Identifier> identifiers = modelProvider.getIdentifiers(document, null);
for (Identifier identifier : identifiers) {
String publicId = identifier.getPublicId();
String systemId = identifier.getSystemId();
fillReferencedGrammarInfo(document, publicId, systemId, identifier, referencedGrammarInfos);
}
}
}
fillReferencedGrammarInfo(document, document.getNamespaceURI(), null, null, referencedGrammarInfos);
return referencedGrammarInfos;
}

private void fillReferencedGrammarInfo(DOMDocument document, String publicId, String systemId,
Identifier identifier, Set<ReferencedGrammarInfo> referencedGrammarInfos) {
ResolvedURIInfo resolvedURIInfo = resolverManager.resolveInfo(document.getDocumentURI(), publicId, systemId);
if (resolvedURIInfo != null) {
GrammarCacheInfo grammarCacheInfo = null;
String cachedResolvedUri = null;
boolean downloading = false;
Exception cacheError = null;
String resolvedUri = resolvedURIInfo.getResolvedURI();
boolean isFileResource = URIUtils.isFileResource(resolvedUri);
if (!isFileResource && cacheResolverExtension.isUseCache()) {
// The DTD/XML Schema comes from http://, ftp:// etc and cache manager is
// activated
// Try to load the DTD/XML Schema with the cache manager
try {
Path file = cacheResolverExtension.getCachedResource(resolvedUri);
if (file != null) {
cachedResolvedUri = file.toUri().toString();
}
} catch (CacheResourceDownloadingException e) {
// the DTD/XML Schema is downloading
downloading = true;
} catch (Exception e) {
// other error like network which is not available
cacheError = e;
} finally {
if (cachedResolvedUri == null) {
try {
cachedResolvedUri = CacheResourcesManager.getResourceCachePath(resolvedUri).toUri()
.toString();
} catch (IOException e) {
// should never occur
}
}
}
grammarCacheInfo = new GrammarCacheInfo(cachedResolvedUri, downloading, cacheError);
}
referencedGrammarInfos.add(new ReferencedGrammarInfo(resolvedURIInfo, grammarCacheInfo, identifier));
}
}

/**
* Returns the content model document loaded by the given uri and null
* otherwise.
Expand Down
Expand Up @@ -15,6 +15,7 @@
import java.util.Collection;

import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.dom.DOMRange;

/**
* Content model provider API.
Expand All @@ -28,9 +29,15 @@ public class Identifier {

private final String systemId;

public Identifier(String publicId, String systemId) {
private final DOMRange range;

private final String kind;

public Identifier(String publicId, String systemId, DOMRange range, String kind) {
this.publicId = publicId;
this.systemId = systemId;
this.range = range;
this.kind = kind;
}

public String getPublicId() {
Expand All @@ -41,6 +48,14 @@ public String getSystemId() {
return systemId;
}

public DOMRange getRange() {
return range;
}

public String getKind() {
return kind;
}

}

/**
Expand Down
@@ -0,0 +1,56 @@
/*******************************************************************************
* Copyright (c) 2020 Red Hat Inc. and others.
* All rights reserved. This program and the accompanying materials
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.lemminx.extensions.contentmodel.model;

/**
* Grammar cache information.
*/
public class GrammarCacheInfo {

private final String cachedResolvedUri;
private final boolean downloading;
private final Exception cacheError;

public GrammarCacheInfo(String cachedResolvedUri, boolean downloading, Exception cacheError) {
super();
this.cachedResolvedUri = cachedResolvedUri;
this.downloading = downloading;
this.cacheError = cacheError;
}

/**
* Returns the file path cache.
*
* @return the file path cache.
*/
public String getCachedResolvedUri() {
return cachedResolvedUri;
}

/**
* Returns true if the grammar is downloading and false otherwise.
*
* @return true if the grammar is downloading and false otherwise.
*/
public boolean isDownloading() {
return downloading;
}

/**
* Returns the error while downloading and null otherwise.
*
* @return the error while downloading and null otherwise.
*/
public Exception getCacheError() {
return cacheError;
}
}

0 comments on commit 7fe05f1

Please sign in to comment.