diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/dtd/contentmodel/CMDTDDocument.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/dtd/contentmodel/CMDTDDocument.java index 87d1174ef0..1528255656 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/dtd/contentmodel/CMDTDDocument.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/dtd/contentmodel/CMDTDDocument.java @@ -22,9 +22,9 @@ import java.util.Map; import java.util.Set; +import org.apache.xerces.impl.XMLEntityManager.ScannedEntity; import org.apache.xerces.impl.dtd.DTDGrammar; import org.apache.xerces.impl.dtd.XMLDTDLoader; -import org.apache.xerces.impl.dtd.XMLEntityDecl; import org.apache.xerces.xni.Augmentations; import org.apache.xerces.xni.XMLString; import org.apache.xerces.xni.XNIException; @@ -90,6 +90,63 @@ public String getComment(String attrName) { } } + public static class ScannedDTDEntityDecl extends DTDEntityDecl { + + private final String entityName; + private final String systemId; + + private final String value; + + private final int startNameLineNumber; + + private final int startNameColumnNumber; + + public ScannedDTDEntityDecl(String name, String value, ScannedEntity scannedEntity) { + super(-1, -1); + this.entityName = name; + this.systemId = scannedEntity.entityLocation.getExpandedSystemId(); + this.value = value; + this.startNameLineNumber = scannedEntity.lineNumber - 1; + this.startNameColumnNumber = getEntityNameStartColumnNumber(entityName, scannedEntity); + } + + @Override + public String getNodeName() { + return entityName; + } + + @Override + public String getNotationName() { + return value; + } + + @Override + public String getSystemId() { + return systemId; + } + + public int getStartNameColumnNumber() { + return startNameColumnNumber; + } + + public int getStartNameLineNumber() { + return startNameLineNumber; + } + + private static int getEntityNameStartColumnNumber(String name, ScannedEntity scannedEntity) { + int endEntityIndex = scannedEntity.position; + int startLineIndex = endEntityIndex - scannedEntity.columnNumber + 1; + char[] ch = scannedEntity.ch; + for (int i = startLineIndex; i < endEntityIndex; i++) { + char c = ch[i]; + if (c == name.charAt(0)) { + return i - startLineIndex; + } + } + return scannedEntity.columnNumber; + } + } + private final String uri; private Map hierarchiesMap; @@ -102,7 +159,7 @@ public String getComment(String attrName) { private Map attributes; private DTDNodeInfo nodeInfo; - private List entities; + private final List entities; public CMDTDDocument() { this(null); @@ -110,6 +167,7 @@ public CMDTDDocument() { public CMDTDDocument(String uri) { this.uri = uri; + this.entities = new ArrayList<>(); } @Override @@ -175,6 +233,13 @@ private CMElementDeclaration findElementDeclaration(String tag, String namespace return null; } + @Override + public void internalEntityDecl(String name, XMLString text, XMLString nonNormalizedText, Augmentations augs) + throws XNIException { + super.internalEntityDecl(name, text, nonNormalizedText, augs); + entities.add(new ScannedDTDEntityDecl(name, text.toString(), fEntityManager.getCurrentEntity())); + } + @Override public void startContentModel(String elementName, Augmentations augs) throws XNIException { if (hierarchiesMap == null) { @@ -304,54 +369,6 @@ public boolean isDirty() { @Override public List getEntities() { - if (entities == null) { - entities = computeEntities(); - } - return entities; - } - - private synchronized List computeEntities() { - if (entities != null) { - return entities; - } - List entities = new ArrayList<>(); - fillEntities(fDTDGrammar, entities); return entities; } - - /** - * Collect entities declared in the DTD grammar. - * - * @param grammar the DTD grammar. - * @param entities list to fill. - */ - private static void fillEntities(DTDGrammar grammar, List entities) { - int index = 0; - XMLEntityDecl entityDecl = new XMLEntityDecl() { - - @Override - public void setValues(String name, String publicId, String systemId, String baseSystemId, String notation, - String value, boolean isPE, boolean inExternal) { - if (inExternal && !isPE) { - // Only external entities (entities declared in the DTD) and not entity with % - // must be collected. - Entity entity = new DTDEntityDecl(0, 0) { - @Override - public String getNodeName() { - return name; - } - - @Override - public String getNotationName() { - return value; - } - }; - entities.add(entity); - } - }; - }; - while (grammar.getEntityDecl(index, entityDecl)) { - index++; - } - } } diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/entities/participants/EntitiesDefinitionParticipant.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/entities/participants/EntitiesDefinitionParticipant.java index 538ee3d4dc..d23d20f687 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/entities/participants/EntitiesDefinitionParticipant.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/entities/participants/EntitiesDefinitionParticipant.java @@ -25,10 +25,12 @@ import org.eclipse.lemminx.dom.DTDEntityDecl; import org.eclipse.lemminx.extensions.contentmodel.model.CMDocument; import org.eclipse.lemminx.extensions.contentmodel.model.ContentModelManager; +import org.eclipse.lemminx.extensions.dtd.contentmodel.CMDTDDocument.ScannedDTDEntityDecl; import org.eclipse.lemminx.services.extensions.AbstractDefinitionParticipant; import org.eclipse.lemminx.services.extensions.IDefinitionRequest; import org.eclipse.lemminx.utils.XMLPositionUtility; import org.eclipse.lsp4j.LocationLink; +import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.jsonrpc.CancelChecker; import org.w3c.dom.Entity; @@ -117,9 +119,19 @@ private static void searchInExternalEntities(String entityName, Range entityRang List entities = cmDocument.getEntities(); for (Entity entity : entities) { if (entityName.equals(entity.getNodeName())) { - // TODO : retrieve location from the external entity? + ScannedDTDEntityDecl entityDecl = (ScannedDTDEntityDecl) entity; + locations.add(createLocationLink(entityRange, entityDecl)); } } } } + + public static LocationLink createLocationLink(Range originSelectionRange, ScannedDTDEntityDecl entityDecl) { + Range targetRange = new Range( + new Position(entityDecl.getStartNameLineNumber(), entityDecl.getStartNameColumnNumber()), + new Position(entityDecl.getStartNameLineNumber(), + entityDecl.getStartNameColumnNumber() + entityDecl.getNodeName().length())); + Range targetSelectionRange = targetRange; + return new LocationLink(entityDecl.getSystemId(), targetRange, targetSelectionRange, originSelectionRange); + } }