diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMAttr.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMAttr.java index 9c7c395537..a9b462fe0e 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMAttr.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMAttr.java @@ -35,6 +35,9 @@ public class DOMAttr extends DOMNode implements org.w3c.dom.Attr { private boolean hasDelimiter; // has '=' + public static final String XMLNS_ATTR = "xmlns"; + public static final String XMLNS_NO_DEFAULT_ATTR = "xmlns:"; + class AttrNameOrValue extends DOMNode { private final DOMAttr ownerAttr; @@ -100,6 +103,15 @@ public String getName() { return name; } + @Override + public String getLocalName() { + int colonIndex = name.indexOf(":"); + if(colonIndex > 0) { + return name.substring(colonIndex + 1); + } + return name; + } + /* * (non-Javadoc) * @@ -238,6 +250,56 @@ public void setNodeAttrValue(DOMNode nodeAttrValue) { public boolean valueContainsOffset(int offset) { return nodeAttrValue != null && offset >= nodeAttrValue.getStart() && offset < nodeAttrValue.getEnd(); } + + /** + * Returns true if attribute name is a xmlns attribute and false otherwise. + * + * @param attributeName + * @return true if attribute name is a xmlns attribute and false otherwise. + */ + public boolean isXmlns() { + return name.startsWith(XMLNS_ATTR); + } + + /** + * Returns true if attribute name is the default xmlns attribute and false + * otherwise. + * + * @param attributeName + * @return true if attribute name is the default xmlns attribute and false + * otherwise. + */ + public boolean isDefaultXmlns() { + return name.equals(XMLNS_ATTR); + } + + public String extractPrefixFromXmlns() { + if (isDefaultXmlns()) { + return name.substring(XMLNS_ATTR.length(), name.length()); + } + return name.substring(XMLNS_NO_DEFAULT_ATTR.length(), name.length()); + } + + /** + * Returns the prefix if the given URI matches this attributes value. + * + * If the URI doesnt match, null is returned. + * @param uri + * @return + */ + public String getPrefixIfMatchesURI(String uri) { + if (isXmlns()) { + if (quotelessValue != null && quotelessValue.equals(uri)) { + if (isDefaultXmlns()) { + // xmlns="http://" + return null; + } + // xmlns:xxx="http://" + return extractPrefixFromXmlns(); + } + } + return null; + } /* * (non-Javadoc) diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMElement.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMElement.java index d18cfc32e0..433039171a 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMElement.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMElement.java @@ -10,6 +10,9 @@ */ package org.eclipse.lsp4xml.dom; +import static org.eclipse.lsp4xml.dom.DOMAttr.XMLNS_ATTR; +import static org.eclipse.lsp4xml.dom.DOMAttr.XMLNS_NO_DEFAULT_ATTR; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -25,8 +28,7 @@ */ public class DOMElement extends DOMNode implements org.w3c.dom.Element { - private static final String XMLNS_ATTR = "xmlns"; - private static final String XMLNS_NO_DEFAULT_ATTR = "xmlns:"; + String tag; boolean selfClosed; @@ -148,7 +150,7 @@ public Collection getAllPrefixes() { for (DOMAttr attr : getAttributeNodes()) { String attributeName = attr.getName(); if (isNoDefaultXmlns(attributeName)) { - prefixes.add(extractPrefixFromXmlns(attributeName)); + prefixes.add(attr.extractPrefixFromXmlns()); } } return prefixes; @@ -156,12 +158,7 @@ public Collection getAllPrefixes() { return Collections.emptyList(); } - private static String extractPrefixFromXmlns(String attributeName) { - if (isDefaultXmlns(attributeName)) { - return attributeName.substring(XMLNS_ATTR.length(), attributeName.length()); - } - return attributeName.substring(XMLNS_NO_DEFAULT_ATTR.length(), attributeName.length()); - } + /** * Returns the xmlns prefix from the given namespave URI and null otherwise. @@ -175,17 +172,9 @@ public String getPrefix(String namespaceURI) { } if (hasAttributes()) { for (DOMAttr attr : getAttributeNodes()) { - String attributeName = attr.getName(); - if (isXmlns(attributeName)) { - String namespace = attr.getValue(); - if (namespace != null && namespace.equals(namespaceURI)) { - if (isDefaultXmlns(attributeName)) { - // xmlns="http://" - return ""; - } - // xmlns:xxx="http://" - return extractPrefixFromXmlns(attributeName); - } + String prefix = attr.getPrefixIfMatchesURI(namespaceURI); + if(prefix != null) { + return prefix; } } } @@ -204,27 +193,7 @@ public String getPrefix(String namespaceURI) { return null; } - /** - * Returns true if attribute name is a xmlns attribute and false otherwise. - * - * @param attributeName - * @return true if attribute name is a xmlns attribute and false otherwise. - */ - private static boolean isXmlns(String attributeName) { - return attributeName.startsWith(XMLNS_ATTR); - } - - /** - * Returns true if attribute name is the default xmlns attribute and false - * otherwise. - * - * @param attributeName - * @return true if attribute name is the default xmlns attribute and false - * otherwise. - */ - private static boolean isDefaultXmlns(String attributeName) { - return attributeName.equals(XMLNS_ATTR); - } + /** * Returns true if attribute name is the no default xmlns attribute and false diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/participants/ContentModelCompletionParticipant.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/participants/ContentModelCompletionParticipant.java index d7d1922261..d4c01cbb27 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/participants/ContentModelCompletionParticipant.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/contentmodel/participants/ContentModelCompletionParticipant.java @@ -10,8 +10,6 @@ */ package org.eclipse.lsp4xml.extensions.contentmodel.participants; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Collection; import org.eclipse.lsp4j.CompletionItem; @@ -33,7 +31,6 @@ import org.eclipse.lsp4xml.services.extensions.ICompletionResponse; import org.eclipse.lsp4xml.settings.SharedSettings; import org.eclipse.lsp4xml.uriresolver.CacheResourceDownloadingException; -import org.eclipse.lsp4xml.utils.StringUtils; /** * Extension to support XML completion based on content model (XML Schema @@ -112,27 +109,9 @@ private void fillWithChildrenElementDeclaration(DOMElement element, Collection hierarchies; private String uri; - public CMDTDDocument() {} public CMDTDDocument(String uri) { @@ -68,11 +67,12 @@ public Collection getElements() { return elements; } - @Override + /** * Returns the URI of this document, is none was provided this * returns null. */ + @Override public String getURI() { return uri; } diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/CMXSDContentModelProvider.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/CMXSDContentModelProvider.java index 39f745ba1e..1ce365a70e 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/CMXSDContentModelProvider.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/CMXSDContentModelProvider.java @@ -73,8 +73,7 @@ public CMDocument createCMDocument(String key) { XSModel model = getLoader().loadURI(key); if (model != null) { // XML Schema can be loaded - CMXSDDocument document = new CMXSDDocument(model, key); - return document; + return new CMXSDDocument(model, key); } return null; } diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/CMXSDDocument.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/CMXSDDocument.java index c7939c22de..6ba9538fef 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/CMXSDDocument.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/extensions/xsd/contentmodel/CMXSDDocument.java @@ -51,8 +51,7 @@ public CMXSDDocument(XSModel model) { } public CMXSDDocument(XSModel model, String uri) { - this.model = model; - this.elementMappings = new HashMap<>(); + this(model); this.uri = uri; } diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLHighlighting.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLHighlighting.java index 4e0f6504d2..2e3e148427 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLHighlighting.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLHighlighting.java @@ -10,6 +10,10 @@ */ package org.eclipse.lsp4xml.services; +import static org.eclipse.lsp4xml.utils.XMLPositionUtility.covers; +import static org.eclipse.lsp4xml.utils.XMLPositionUtility.doesTagCoverPosition; +import static org.eclipse.lsp4xml.utils.XMLPositionUtility.getTagNameRange; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -24,11 +28,8 @@ import org.eclipse.lsp4xml.dom.DOMDocument; import org.eclipse.lsp4xml.dom.DOMElement; import org.eclipse.lsp4xml.dom.DOMNode; -import org.eclipse.lsp4xml.dom.parser.Scanner; import org.eclipse.lsp4xml.dom.parser.TokenType; -import org.eclipse.lsp4xml.dom.parser.XMLScanner; import org.eclipse.lsp4xml.services.extensions.XMLExtensionsRegistry; - /** * XML highlighting support. * @@ -91,11 +92,6 @@ public List findDocumentHighlights(DOMDocument xmlDocument, P return Collections.emptyList(); } - private static boolean doesTagCoverPosition(Range startTagRange, Range endTagRange, Position position) { - return startTagRange != null && covers(startTagRange, position) - || endTagRange != null && covers(endTagRange, position); - } - private static List getHighlightsList(Range startTagRange, Range endTagRange) { List result = new ArrayList<>(2); @@ -108,34 +104,4 @@ private static List getHighlightsList(Range startTagRange, Ra return result; } - private static boolean isBeforeOrEqual(Position pos1, Position pos2) { - return pos1.getLine() < pos2.getLine() - || (pos1.getLine() == pos2.getLine() && pos1.getCharacter() <= pos2.getCharacter()); - } - - private static boolean covers(Range range, Position position) { - return isBeforeOrEqual(range.getStart(), position) && isBeforeOrEqual(position, range.getEnd()); - } - - private static Range getTagNameRange(TokenType tokenType, int startOffset, DOMDocument xmlDocument) { - - Scanner scanner = XMLScanner.createScanner(xmlDocument.getText(), startOffset); - - TokenType token = scanner.scan(); - while (token != TokenType.EOS && token != tokenType) { - token = scanner.scan(); - } - if (token != TokenType.EOS) { - try { - return new Range(xmlDocument.positionAt(scanner.getTokenOffset()), - xmlDocument.positionAt(scanner.getTokenEnd())); - } catch (BadLocationException e) { - LOGGER.log(Level.SEVERE, - "While creating Range in XMLHighlighting the Scanner's Offset was a BadLocation", e); - return null; - } - } - return null; - } - } diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLLanguageService.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLLanguageService.java index 10906e543a..f6d62fb6e6 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLLanguageService.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLLanguageService.java @@ -12,13 +12,10 @@ import java.nio.file.Path; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; import java.util.function.Consumer; -import java.util.stream.Collectors; import org.eclipse.lsp4j.CodeAction; import org.eclipse.lsp4j.CodeActionContext; @@ -46,7 +43,6 @@ import org.eclipse.lsp4xml.dom.DOMDocument; import org.eclipse.lsp4xml.dom.DOMElement; import org.eclipse.lsp4xml.extensions.contentmodel.settings.XMLValidationSettings; -import org.eclipse.lsp4xml.services.extensions.CompletionSettings; import org.eclipse.lsp4xml.services.extensions.XMLExtensionsRegistry; import org.eclipse.lsp4xml.settings.SharedSettings; import org.eclipse.lsp4xml.settings.XMLFormattingOptions; @@ -70,6 +66,7 @@ public class XMLLanguageService extends XMLExtensionsRegistry { private XMLDefinition definition; private XMLReference reference; private final XMLCodeActions codeActions; + private final XMLRename rename; public XMLLanguageService() { this.formatter = new XMLFormatter(this); @@ -83,6 +80,7 @@ public XMLLanguageService() { this.definition = new XMLDefinition(this); this.reference = new XMLReference(this); this.codeActions = new XMLCodeActions(this); + this.rename = new XMLRename(this); } public List format(TextDocument document, Range range, XMLFormattingOptions options) { @@ -164,11 +162,7 @@ public List getFoldingRanges(TextDocument document, FoldingRangeCa } public WorkspaceEdit doRename(DOMDocument xmlDocument, Position position, String newText) { - List textEdits = findDocumentHighlights(xmlDocument, position).stream() - .map(h -> new TextEdit(h.getRange(), newText)).collect(Collectors.toList()); - Map> changes = new HashMap<>(); - changes.put(xmlDocument.getDocumentURI(), textEdits); - return new WorkspaceEdit(changes); + return rename.doRename(xmlDocument, position, newText); } public List findDocumentLinks(DOMDocument document) { diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLRename.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLRename.java new file mode 100644 index 0000000000..a3a0266be8 --- /dev/null +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLRename.java @@ -0,0 +1,299 @@ +/******************************************************************************* +* 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 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ + +package org.eclipse.lsp4xml.services; + +import static org.eclipse.lsp4xml.utils.XMLPositionUtility.covers; +import static org.eclipse.lsp4xml.utils.XMLPositionUtility.doesTagCoverPosition; +import static org.eclipse.lsp4xml.utils.XMLPositionUtility.getTagNameRange; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.annotation.Nullable; + +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.TextEdit; +import org.eclipse.lsp4j.WorkspaceEdit; +import org.eclipse.lsp4xml.commons.BadLocationException; +import org.eclipse.lsp4xml.dom.DOMAttr; +import org.eclipse.lsp4xml.dom.DOMDocument; +import org.eclipse.lsp4xml.dom.DOMElement; +import org.eclipse.lsp4xml.dom.DOMNode; +import org.eclipse.lsp4xml.dom.parser.TokenType; +import org.eclipse.lsp4xml.services.extensions.XMLExtensionsRegistry; + +/** + * Handle all rename requests + * + * Author: + * Nikolas Komonen - nkomonen@redhat.com + */ +public class XMLRename { + + private static final Logger LOGGER = Logger.getLogger(XMLRename.class.getName()); + + private final XMLExtensionsRegistry extensionsRegistry; + + public XMLRename(XMLExtensionsRegistry extensionsRegistry) { + this.extensionsRegistry = extensionsRegistry; + } + + public WorkspaceEdit doRename(DOMDocument xmlDocument, Position position, String newText) { + List textEdits = getRenameTextEdits(xmlDocument, position, newText); + Map> changes = new HashMap<>(); + changes.put(xmlDocument.getDocumentURI(), textEdits); + return new WorkspaceEdit(changes); + } + + public List getRenameTextEdits(DOMDocument xmlDocument, Position position, String newText) { + int offset = -1; + try { + offset = xmlDocument.offsetAt(position); + } catch (BadLocationException e) { + LOGGER.log(Level.SEVERE, "In XMLHighlighting the client provided Position is at a BadLocation", e); + return Collections.emptyList(); + } + DOMNode node = xmlDocument.findNodeAt(offset); + if (node == null || !node.isElement() || ((DOMElement) node).getTagName() == null) { + return Collections.emptyList(); + } + Range startTagRange; + Range endTagRange; + if(node.isCDATA()) { + Position startPos = null; + Position endPos = null; + Range tempRange = null; + try { + startPos = xmlDocument.positionAt(node.getStart()); + endPos = xmlDocument.positionAt(node.getEnd()); + tempRange = new Range(startPos, endPos); + + } catch (BadLocationException e) { + LOGGER.log(Level.SEVERE, "In XMLRename the Node at provided Offset is a BadLocation", e); + return Collections.emptyList(); + } + if (covers(tempRange, position)) { + startPos.setCharacter(startPos.getCharacter() + 1); // {Cursor} <{Cursor}![CDATA[ + endPos.setCharacter(endPos.getCharacter() - 1); // ]]>{Cursor} -> ]]{Cursor}> + Position startPosEnd = new Position(startPos.getLine(), startPos.getCharacter() + 8); + Position endPosStart = new Position(endPos.getLine(), endPos.getCharacter() - 2); + startTagRange = new Range(startPos, startPosEnd); + endTagRange = new Range(endPosStart, endPos); + return getRenameList(startTagRange, endTagRange, newText); + } + return Collections.emptyList(); + } + else if(node.isElement()) { + DOMElement element = (DOMElement) node; + startTagRange = getTagNameRange(TokenType.StartTag, node.getStart(), xmlDocument); + endTagRange = element.hasEndTag() ? getTagNameRange(TokenType.EndTag, element.getEndTagOpenOffset(), xmlDocument) + : null; + if (doesTagCoverPosition(startTagRange, endTagRange, position)) { + //Check if xsd namespace rename + String fullNodeName = node.getNodeName(); + int indexOfColon = fullNodeName.indexOf(":"); + if(indexOfColon > 0) { + Position startTagStartPosition = startTagRange.getStart(); + Position startTagPrefixPosition = new Position(startTagStartPosition.getLine(), startTagStartPosition.getCharacter() + indexOfColon); + + Position endTagStartPosition = endTagRange.getStart(); + Position endTagPrefixPosition = new Position(endTagStartPosition.getLine(), endTagStartPosition.getCharacter() + indexOfColon); + + Range startTagPrefixRange = new Range(startTagStartPosition, startTagPrefixPosition); + Range endTagPrefixRange = new Range(endTagStartPosition, endTagPrefixPosition); + + if(doesTagCoverPosition(startTagPrefixRange, endTagPrefixRange, position)) {// Element prefix rename + String prefix = element.getPrefix(); + return renameElementNamespace(xmlDocument, element, prefix.length(), newText); + } + else { //suffix rename without wiping namespace + String suffixName = element.getLocalName(); + int suffixLength = suffixName.length(); + Position startTagEndPosition = startTagRange.getEnd(); + Position suffixStartPositionStart = new Position(startTagEndPosition.getLine(), startTagEndPosition.getCharacter() - suffixLength); + + Position endTagEndPosition = endTagRange.getEnd(); + Position suffixEndPositionStart = new Position(endTagEndPosition.getLine(), endTagEndPosition.getCharacter() - suffixLength); + + Range suffixRangeStart = new Range(suffixStartPositionStart, startTagEndPosition); + Range suffixRangeEnd = new Range(suffixEndPositionStart, endTagEndPosition); + + return getRenameList(suffixRangeStart, suffixRangeEnd, newText); + } + } + //Regular tag name rename + return getRenameList(startTagRange, endTagRange, newText); + } + + if(element.equals(xmlDocument.getDocumentElement())) { // If attribute xmlns:ATT_NAME was renamed + List attributes = element.getAttributeNodes(); + + if(attributes == null) { + return Collections.emptyList(); + } + + for (DOMAttr attr : attributes) { + DOMNode nameNode = attr.getNodeAttrName(); + + if(!attr.isXmlns()) { + continue; + } + + Position start; + Position end; + try { + start = xmlDocument.positionAt(nameNode.getStart()); + end = xmlDocument.positionAt(nameNode.getEnd()); + } catch (BadLocationException e) { + continue; + } + + if(covers(new Range(start, end), position)) { + String namespaceName = attr.getLocalName(); + return renameAllNamespaceOccurrences(xmlDocument, namespaceName, newText, attr); + } + } + } + } + + return Collections.emptyList(); + } + + private static List getRenameList(Range startTagRange, Range endTagRange, String newText) { + List result = new ArrayList<>(2); + if (startTagRange != null) { + result.add(new TextEdit(startTagRange, newText)); + } + if (endTagRange != null) { + result.add(new TextEdit(endTagRange, newText)); + } + return result; + } + + private static List renameAllNamespaceOccurrences(DOMDocument document, String oldNamespace, String newNamespace, @Nullable DOMAttr rootAttr) { + DOMElement rootElement = document.getDocumentElement(); + + List edits = new ArrayList(); + + // Renames the xmlns:NAME_SPACE attribute + if(rootAttr != null) { + Position start; + try { + start = document.positionAt(rootAttr.getStart() + "xmlns:".length()); + } catch (BadLocationException e) { + start = null; + } + + if(start != null) { + Position end = new Position(start.getLine(), start.getCharacter() + oldNamespace.length()); + edits.add(new TextEdit(new Range(start, end), newNamespace)); + } + } + + //Renames all elements with oldNamespace + List children = Arrays.asList(rootElement); + return renameElementsNamespace(document, edits, children, oldNamespace, newNamespace); + } + + private static List renameElementsNamespace(DOMDocument document, List edits, List elements, String oldNamespace, String newNamespace) { + int oldNamespaceLength = oldNamespace.length(); + for (DOMNode node : elements) { + if(node.isElement()) { + DOMElement element = (DOMElement) node; + if(oldNamespace.equals(element.getPrefix())) { + edits.addAll(renameElementNamespace(document, element, oldNamespaceLength, newNamespace)); + } + if(element.hasAttributes()) { + edits.addAll(renameElementAttributeValueNamespace(document, element, oldNamespace, newNamespace)); + } + + if(element.hasChildNodes()) { + renameElementsNamespace(document, edits, element.getChildren(), oldNamespace, newNamespace); + } + } + } + + return edits; + + } + + private static List renameElementNamespace(DOMDocument document, DOMElement element, int oldNamespaceLength, String newNamespace) { + List edits = new ArrayList(); + Range[] ranges = createNamespaceRange(document, element, oldNamespaceLength); + if(ranges == null) { + return edits; + } + for (Range r : ranges) { + if(r != null) { + edits.add(new TextEdit(r, newNamespace)); + } + } + return edits; + } + + private static List renameElementAttributeValueNamespace(DOMDocument document, DOMElement element, String oldNamespace, String newNamespace) { + + List attributes = element.getAttributeNodes(); + List edits = new ArrayList(); + if(attributes != null) { + for (DOMAttr attr : attributes) { + DOMNode attrValue = attr.getNodeAttrValue(); + if(attrValue != null) { + String attrValueText = attr.getValue(); + if(attrValueText != null && attrValueText.startsWith(oldNamespace + ":")) { + int startOffset = attrValue.getStart() + 1; + + Position start,end; + try { + start = document.positionAt(startOffset); + end = new Position(start.getLine(), start.getCharacter() + oldNamespace.length()); + } catch (BadLocationException e) { + return edits; + } + edits.add(new TextEdit(new Range(start, end), newNamespace)); + } + } + } + } + return edits; + } + + private static Range[] createNamespaceRange(DOMDocument document, DOMElement element, int namespaceLength) { + Range[] ranges = new Range[2]; + + Position start; + Position end; + try { + if(element.hasStartTag()) { + int startName = element.getStart() + 1; //skip '<' + start = document.positionAt(startName); + end = new Position(start.getLine(), start.getCharacter() + namespaceLength); + ranges[0] = new Range(start, end); + } + if(element.hasEndTag()) { + int startName = element.getEndTagOpenOffset() + 2; //skip '", "newText", edits("newText", r(0, 1, 5), r(0, 15, 19))); } + @Test + public void testNamespaceRename() throws BadLocationException { + String xml = + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + assertRename(xml, "ns", edits("ns", r(0, 12, 14), r(0, 1, 3), r(6, 2, 4), //root attribute, start tag, end tag + r(1, 3, 5), r(1, 10, 12), + r(2, 3, 5), r(2, 10, 12), + r(3, 3, 5), //\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + assertRename(xml, "ns", edits("ns", r(2, 3, 5), r(2, 10, 12))); + } + + @Test + public void testNamespaceRenameStartTagPrefix() throws BadLocationException { + String xml = + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + assertRename(xml, "ns", edits("ns", r(2, 3, 5), r(2, 10, 12))); + } + + @Test + public void testNamespaceRenameEndTagSuffix() throws BadLocationException { + String xml = + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + assertRename(xml, "BB", edits("BB", r(2, 6, 7), r(2, 13, 14))); + } + + @Test + public void testNamespaceRenameStartTagSuffix() throws BadLocationException { + String xml = + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + assertRename(xml, "BB", edits("BB", r(2, 6, 7), r(2, 13, 14))); + } + private void assertRename(String value, String newText) throws BadLocationException { assertRename(value, newText, Collections.emptyList()); } diff --git a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/utils/FilesUtilsTest.java b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/utils/FilesUtilsTest.java index fffb75827e..4d47f6a94a 100644 --- a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/utils/FilesUtilsTest.java +++ b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/utils/FilesUtilsTest.java @@ -38,9 +38,9 @@ public void testFilesCachePathPreference() throws Exception { @Test public void normalizePathTest() { assertEquals(Paths.get(System.getProperty("user.home"), "Test", "Folder").toString(), FilesUtils.normalizePath("~/Test/Folder")); - assertEquals(Paths.get(separator, "Test", "~", "Folder").toString(), FilesUtils.normalizePath("/Test/~/Folder")); + assertEquals(Paths.get(separator + "Test", "~", "Folder").toString(), FilesUtils.normalizePath("/Test/~/Folder")); assertEquals(Paths.get("~", "Test", "Folder").toString(), FilesUtils.normalizePath("./~/Test/Folder")); - assertEquals(Paths.get(separator, "Folder").toString(), FilesUtils.normalizePath("/Test/../Folder")); - assertEquals(Paths.get(separator, "Users", "Nikolas").toString(), FilesUtils.normalizePath("\\Users\\Nikolas\\")); + assertEquals(Paths.get(separator + "Folder").toString(), FilesUtils.normalizePath("/Test/../Folder")); + assertEquals(Paths.get(separator + "Users", "Nikolas").toString(), FilesUtils.normalizePath("\\Users\\Nikolas\\")); } -} \ No newline at end of file +}