diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/XMLTextDocumentService.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/XMLTextDocumentService.java index 0c826f377a..17d7bb28e1 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/XMLTextDocumentService.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/XMLTextDocumentService.java @@ -78,6 +78,8 @@ import org.eclipse.lsp4j.FoldingRangeRequestParams; import org.eclipse.lsp4j.Hover; import org.eclipse.lsp4j.HoverParams; +import org.eclipse.lsp4j.LinkedEditingRangeParams; +import org.eclipse.lsp4j.LinkedEditingRanges; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.LocationLink; import org.eclipse.lsp4j.PublishDiagnosticsParams; @@ -450,6 +452,13 @@ public CompletableFuture>> codeAction(CodeActio }); } + @Override + public CompletableFuture linkedEditingRange(LinkedEditingRangeParams params) { + return computeDOMAsync(params.getTextDocument(), (cancelChecker, xmlDocument) -> { + return getXMLLanguageService().findLinkedEditingRanges(xmlDocument, params.getPosition(), cancelChecker); + }); + } + @Override public void didSave(DidSaveTextDocumentParams params) { computeAsync((monitor) -> { diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/commons/snippets/SnippetRegistry.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/commons/snippets/SnippetRegistry.java index c14c207aac..2fca7c2bc7 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/commons/snippets/SnippetRegistry.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/commons/snippets/SnippetRegistry.java @@ -263,7 +263,7 @@ public List getCompletionItems(Range replaceRange, String lineDe range = new Range(replaceRange.getStart(), end); } } - item.setTextEdit(new TextEdit(range, insertText)); + item.setTextEdit(Either.forLeft(new TextEdit(range, insertText))); item.setInsertTextFormat(InsertTextFormat.Snippet); item.setSortText(snippet.getSortText()); return item; diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/DOMElement.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/DOMElement.java index 8cc4256fa6..d85b6e037e 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/DOMElement.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/dom/DOMElement.java @@ -287,11 +287,15 @@ public boolean isInStartTag(int offset) { } public boolean isInEndTag(int offset) { + return isInEndTag(offset, false); + } + + public boolean isInEndTag(int offset, boolean afterBackSlash) { if (endTagOpenOffset == NULL_VALUE) { // case >| return false; } - if (offset > endTagOpenOffset && offset < getEnd()) { + if (offset > endTagOpenOffset + (afterBackSlash ? 1 : 0) && offset < getEnd()) { // case <\bean | > return true; } diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/ContentModelCompletionParticipant.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/ContentModelCompletionParticipant.java index 1463017c83..c1d6f715cc 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/ContentModelCompletionParticipant.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/ContentModelCompletionParticipant.java @@ -40,6 +40,7 @@ import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.TextEdit; import org.eclipse.lsp4j.jsonrpc.CancelChecker; +import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -219,7 +220,7 @@ private static void addTagName(NodeList list, Set tags, ICompletionReque item.setKind(CompletionItemKind.Property); item.setFilterText(request.getFilterForStartTagName(tagName)); String xml = elt.getOwnerDocument().getText().substring(elt.getStart(), elt.getEnd()); - item.setTextEdit(new TextEdit(request.getReplaceRange(), xml)); + item.setTextEdit(Either.forLeft(new TextEdit(request.getReplaceRange(), xml))); response.addCompletionItem(item); tags.add(item.getLabel()); } @@ -250,7 +251,7 @@ private static void addCompletionItem(CMElementDeclaration elementDeclaration, D item.setDocumentation(documentation); String xml = generator.generate(elementDeclaration, prefix, isGenerateEndTag(request.getNode(), request.getOffset(), label)); - item.setTextEdit(new TextEdit(request.getReplaceRange(), xml)); + item.setTextEdit(Either.forLeft(new TextEdit(request.getReplaceRange(), xml))); item.setInsertTextFormat(InsertTextFormat.Snippet); response.addCompletionItem(item, true); } @@ -349,7 +350,7 @@ private void fillAttributeValuesWithCMAttributeDeclarations(CMElementDeclaration item.setLabel(value); item.setKind(CompletionItemKind.Value); item.setFilterText(insertText); - item.setTextEdit(new TextEdit(fullRange, insertText)); + item.setTextEdit(Either.forLeft(new TextEdit(fullRange, insertText))); MarkupContent documentation = XMLGenerator.createMarkupContent(cmAttribute, value, cmElement, request); item.setDocumentation(documentation); response.addCompletionItem(item); @@ -390,7 +391,7 @@ public void onXMLContent(ICompletionRequest request, ICompletionResponse respons item.setLabel(value); item.setKind(CompletionItemKind.Value); item.setFilterText(tokenStart + insertText); - item.setTextEdit(new TextEdit(fullRange, insertText)); + item.setTextEdit(Either.forLeft(new TextEdit(fullRange, insertText))); MarkupContent documentation = XMLGenerator.createMarkupContent(cmElement, value, request); item.setDocumentation(documentation); response.addCompletionItem(item); diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/entities/participants/EntitiesCompletionParticipant.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/entities/participants/EntitiesCompletionParticipant.java index f47bf67835..de7fc9bc54 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/entities/participants/EntitiesCompletionParticipant.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/entities/participants/EntitiesCompletionParticipant.java @@ -36,6 +36,7 @@ import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.TextEdit; import org.eclipse.lsp4j.jsonrpc.CancelChecker; +import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.w3c.dom.Entity; import org.w3c.dom.NamedNodeMap; @@ -143,7 +144,7 @@ private static void fillCompletion(String name, MarkupContent documentation, Ran item.setInsertTextFormat(InsertTextFormat.PlainText); String insertText = entityName; item.setFilterText(insertText); - item.setTextEdit(new TextEdit(entityRange, insertText)); + item.setTextEdit(Either.forLeft(new TextEdit(entityRange, insertText))); item.setDocumentation(documentation); response.addCompletionItem(item); } 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 index 9bbb367d1b..9078661cd3 100644 --- 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 @@ -40,6 +40,7 @@ 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: @@ -73,14 +74,15 @@ 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 { + public void onAttributeValue(String value, ICompletionRequest request, ICompletionResponse response, + CancelChecker cancelChecker) throws Exception { // File path completion on attribute value addCompletionItems(value, request, response); } @Override - public void onDTDSystemId(String value, ICompletionRequest request, ICompletionResponse response, CancelChecker cancelChecker) throws Exception { + public void onDTDSystemId(String value, ICompletionRequest request, ICompletionResponse response, + CancelChecker cancelChecker) throws Exception { // File path completion on DTD DOCTYPE SYSTEM addCompletionItems(value, request, response); } @@ -253,7 +255,7 @@ private static void createFilePathCompletionItem(File f, Range replaceRange, ICo item.setSortText(CompletionSortTextHelper.getSortText(kind)); item.setFilterText(insertText); - item.setTextEdit(new TextEdit(replaceRange, insertText)); + item.setTextEdit(Either.forLeft(new TextEdit(replaceRange, insertText))); response.addCompletionItem(item); } diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/prolog/PrologModel.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/prolog/PrologModel.java index 562dc39a12..165ce3057f 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/prolog/PrologModel.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/prolog/PrologModel.java @@ -14,6 +14,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; + import org.eclipse.lemminx.commons.BadLocationException; import org.eclipse.lemminx.dom.DOMAttr; import org.eclipse.lemminx.dom.DOMDocument; @@ -29,6 +30,7 @@ import org.eclipse.lsp4j.MarkupKind; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.TextEdit; +import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.w3c.dom.NamedNodeMap; import com.google.common.base.Charsets; @@ -160,7 +162,7 @@ private static void createCompletionItemsForValues(Collection enumeratio item.setLabel(option); item.setFilterText(insertText); item.setKind(CompletionItemKind.Enum); - item.setTextEdit(new TextEdit(editRange, insertText)); + item.setTextEdit(Either.forLeft(new TextEdit(editRange, insertText))); item.setSortText(Integer.toString(sortText)); sortText++; response.addCompletionItem(item); diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/references/participants/XMLReferencesCompletionParticipant.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/references/participants/XMLReferencesCompletionParticipant.java index 6d3301f63f..74cd1a4f4a 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/references/participants/XMLReferencesCompletionParticipant.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/references/participants/XMLReferencesCompletionParticipant.java @@ -44,7 +44,7 @@ public void onXMLContent(ICompletionRequest request, ICompletionResponse respons item.setKind(CompletionItemKind.Property); item.setDocumentation(Either.forLeft(label)); item.setFilterText(insertText); - item.setTextEdit(new TextEdit(range, insertText)); + item.setTextEdit(Either.forLeft(new TextEdit(range, insertText))); item.setInsertTextFormat(InsertTextFormat.PlainText); response.addCompletionItem(item); }); diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsd/participants/XSDCompletionParticipant.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsd/participants/XSDCompletionParticipant.java index 687b34cf28..1d16adf430 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsd/participants/XSDCompletionParticipant.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsd/participants/XSDCompletionParticipant.java @@ -29,6 +29,7 @@ import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.TextEdit; import org.eclipse.lsp4j.jsonrpc.CancelChecker; +import org.eclipse.lsp4j.jsonrpc.messages.Either; /** * XSD completion for @@ -42,8 +43,8 @@ public class XSDCompletionParticipant extends CompletionParticipantAdapter { @Override - public void onAttributeValue(String valuePrefix, ICompletionRequest request, ICompletionResponse response, CancelChecker cancelChecker) - throws Exception { + public void onAttributeValue(String valuePrefix, ICompletionRequest request, ICompletionResponse response, + CancelChecker cancelChecker) throws Exception { DOMNode node = request.getNode(); DOMDocument document = node.getOwnerDocument(); if (!DOMUtils.isXSD(document)) { @@ -67,7 +68,7 @@ public void onAttributeValue(String valuePrefix, ICompletionRequest request, ICo item.setLabel(value); item.setKind(CompletionItemKind.Value); item.setFilterText(insertText); - item.setTextEdit(new TextEdit(fullRange, insertText)); + item.setTextEdit(Either.forLeft(new TextEdit(fullRange, insertText))); response.addCompletionItem(item); }); if (bindingType.isSimple()) { @@ -83,7 +84,7 @@ public void onAttributeValue(String valuePrefix, ICompletionRequest request, ICo item.setLabel(value); item.setKind(CompletionItemKind.Value); item.setFilterText(insertText); - item.setTextEdit(new TextEdit(fullRange, insertText)); + item.setTextEdit(Either.forLeft(new TextEdit(fullRange, insertText))); response.addCompletionItem(item); }); } diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsi/XSISchemaModel.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsi/XSISchemaModel.java index 8753e55f23..ddcc79ef48 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsi/XSISchemaModel.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/xsi/XSISchemaModel.java @@ -33,6 +33,7 @@ import org.eclipse.lsp4j.MarkupKind; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.TextEdit; +import org.eclipse.lsp4j.jsonrpc.messages.Either; /** * This class holds values that represent the XSI xsd. Can be seen at @@ -41,115 +42,110 @@ public class XSISchemaModel { private static String lineSeparator = System.lineSeparator(); - public static final String TYPE_DOC = "Specifies the type of an element. This attribute labels an element as a particular type, even though there might not be an element declaration in the schema binding that element to the type."; + public static final String TYPE_DOC = "Specifies the type of an element. This attribute labels an element as a particular type, even though there might not be an element declaration in the schema binding that element to the type."; public static final String NIL_DOC = "Indicates if an element should contain content. Valid values are `true` or `false`"; - public static final String SCHEMA_LOCATION_DOC = - "The xsi:schemaLocation attribute can be used in an XML document " + - "to reference an XML Schema document that has a target namespace. " + lineSeparator + - "```xml " + lineSeparator + - "" + lineSeparator + - " " + lineSeparator + - " " + lineSeparator + - "```" ; - public static final String NO_NAMESPACE_SCHEMA_LOCATION_DOC= - "The xsi:noNamespaceSchemaLocation attribute can be used in an XML document " + lineSeparator + - "to reference an XML Schema document that does not have a target namespace. " + lineSeparator + - "```xml " + lineSeparator + - "" + lineSeparator + - " " + lineSeparator + - " " + lineSeparator + - "```" ; + public static final String SCHEMA_LOCATION_DOC = "The xsi:schemaLocation attribute can be used in an XML document " + + "to reference an XML Schema document that has a target namespace. " + lineSeparator + "```xml " + + lineSeparator + "" + lineSeparator + " " + + lineSeparator + " " + lineSeparator + "```"; + public static final String NO_NAMESPACE_SCHEMA_LOCATION_DOC = "The xsi:noNamespaceSchemaLocation attribute can be used in an XML document " + + lineSeparator + "to reference an XML Schema document that does not have a target namespace. " + + lineSeparator + "```xml " + lineSeparator + "" + lineSeparator + " " + lineSeparator + + " " + lineSeparator + "```"; public static final String XSI_WEBSITE = "http://www.w3.org/2001/XMLSchema-instance"; public static final String XSI_DOC = "The namespace that defines important attributes such as `noNamespaceSchemaLocation` and `schemaLocation`."; - public static void computeCompletionResponses(ICompletionRequest request, - ICompletionResponse response, DOMDocument document, boolean generateValue, - SharedSettings sharedSettings) throws BadLocationException { + + public static void computeCompletionResponses(ICompletionRequest request, ICompletionResponse response, + DOMDocument document, boolean generateValue, SharedSettings sharedSettings) throws BadLocationException { Range editRange = request.getReplaceRange(); DOMElement rootElement = document.getDocumentElement(); int offset = document.offsetAt(editRange.getStart()); - if(rootElement == null || offset <= rootElement.getStart() || offset >= rootElement.getEnd()) { + if (rootElement == null || offset <= rootElement.getStart() || offset >= rootElement.getEnd()) { return; } boolean inRootElement = false; - DOMNode nodeAtOffset = document.findNodeAt(offset); + DOMNode nodeAtOffset = document.findNodeAt(offset); DOMElement elementAtOffset; - if(nodeAtOffset != null && nodeAtOffset.isElement()) { + if (nodeAtOffset != null && nodeAtOffset.isElement()) { elementAtOffset = (DOMElement) nodeAtOffset; - } - else { + } else { return; } - if(rootElement.equals(nodeAtOffset)) { + if (rootElement.equals(nodeAtOffset)) { inRootElement = true; } boolean isSnippetsSupported = request.isCompletionSnippetsSupported(); - if(inRootElement) { - if(!hasAttribute(elementAtOffset, "xmlns") && !response.hasAttribute("xmlns")) { // "xmlns" completion - createCompletionItem("xmlns", isSnippetsSupported, generateValue, editRange, null, - null, null, response, sharedSettings); + if (inRootElement) { + if (!hasAttribute(elementAtOffset, "xmlns") && !response.hasAttribute("xmlns")) { // "xmlns" completion + createCompletionItem("xmlns", isSnippetsSupported, generateValue, editRange, null, null, null, response, + sharedSettings); } - if(document.hasSchemaInstancePrefix() == false) { // "xmlns:xsi" completion - createCompletionItem("xmlns:xsi", isSnippetsSupported, generateValue, editRange, XSI_WEBSITE, - null, XSI_DOC, response, sharedSettings); + if (document.hasSchemaInstancePrefix() == false) { // "xmlns:xsi" completion + createCompletionItem("xmlns:xsi", isSnippetsSupported, generateValue, editRange, XSI_WEBSITE, null, + XSI_DOC, response, sharedSettings); return;// All the following completion cases dont exist, so return. } } - + String actualPrefix = document.getSchemaInstancePrefix(); - if(actualPrefix == null) { + if (actualPrefix == null) { return; } - + String name; String documentation; - + boolean schemaLocationExists = document.hasSchemaLocation(); boolean noNamespaceSchemaLocationExists = document.hasNoNamespaceSchemaLocation(); - //Indicates that no values are allowed inside an XML element - if(!hasAttribute(elementAtOffset, actualPrefix, "nil")) { + // Indicates that no values are allowed inside an XML element + if (!hasAttribute(elementAtOffset, actualPrefix, "nil")) { documentation = NIL_DOC; name = actualPrefix + ":nil"; - createCompletionItem(name, isSnippetsSupported, generateValue, editRange, StringUtils.TRUE, - StringUtils.TRUE_FALSE_ARRAY, documentation, response, sharedSettings); + createCompletionItem(name, isSnippetsSupported, generateValue, editRange, StringUtils.TRUE, + StringUtils.TRUE_FALSE_ARRAY, documentation, response, sharedSettings); } - //Signals that an element should be accepted as ·valid· when it has no content despite - //a content type which does not require or even necessarily allow empty content. - //An element may be ·valid· without content if it has the attribute xsi:nil with - //the value true. - if(!hasAttribute(elementAtOffset, actualPrefix, "type")) { + // Signals that an element should be accepted as ·valid· when it has no content + // despite + // a content type which does not require or even necessarily allow empty + // content. + // An element may be ·valid· without content if it has the attribute xsi:nil + // with + // the value true. + if (!hasAttribute(elementAtOffset, actualPrefix, "type")) { documentation = TYPE_DOC; name = actualPrefix + ":type"; - createCompletionItem(name, isSnippetsSupported, generateValue, editRange, null, null, - documentation, response, sharedSettings); + createCompletionItem(name, isSnippetsSupported, generateValue, editRange, null, null, documentation, + response, sharedSettings); } - - if(inRootElement) { - if(!schemaLocationExists) { - //The xsi:schemaLocation and xsi:noNamespaceSchemaLocation attributes can be used in a document - //to provide hints as to the physical location of schema documents which may be used for ·assessment·. + + if (inRootElement) { + if (!schemaLocationExists) { + // The xsi:schemaLocation and xsi:noNamespaceSchemaLocation attributes can be + // used in a document + // to provide hints as to the physical location of schema documents which may be + // used for ·assessment·. documentation = SCHEMA_LOCATION_DOC; name = actualPrefix + ":schemaLocation"; - createCompletionItem(name, isSnippetsSupported, generateValue, editRange, null, null, - documentation, response, sharedSettings); + createCompletionItem(name, isSnippetsSupported, generateValue, editRange, null, null, documentation, + response, sharedSettings); } - if(!noNamespaceSchemaLocationExists) { + if (!noNamespaceSchemaLocationExists) { documentation = NO_NAMESPACE_SCHEMA_LOCATION_DOC; name = actualPrefix + ":noNamespaceSchemaLocation"; - createCompletionItem(name, isSnippetsSupported, generateValue, editRange, null, null, - documentation, response, sharedSettings); - } + createCompletionItem(name, isSnippetsSupported, generateValue, editRange, null, null, documentation, + response, sharedSettings); + } } } private static void createCompletionItem(String attrName, boolean canSupportSnippet, boolean generateValue, Range editRange, String defaultValue, Collection enumerationValues, String documentation, - ICompletionResponse response, SharedSettings sharedSettings){ + ICompletionResponse response, SharedSettings sharedSettings) { CompletionItem item = new AttributeCompletionItem(attrName, canSupportSnippet, editRange, generateValue, defaultValue, enumerationValues, sharedSettings); MarkupContent markup = new MarkupContent(); @@ -159,36 +155,42 @@ private static void createCompletionItem(String attrName, boolean canSupportSnip response.addCompletionItem(item); } - public static void computeValueCompletionResponses(ICompletionRequest request, - ICompletionResponse response, DOMDocument document) throws BadLocationException { + public static void computeValueCompletionResponses(ICompletionRequest request, ICompletionResponse response, + DOMDocument document) throws BadLocationException { Range editRange = request.getReplaceRange(); int offset = document.offsetAt(editRange.getStart()); DOMNode nodeAtOffset = request.getNode(); - + String actualPrefix = document.getSchemaInstancePrefix(); DOMAttr attrAtOffset = nodeAtOffset.findAttrAt(offset); if (attrAtOffset == null) { return; } String attrName = attrAtOffset.getName(); - - if(attrName != null) { - if(actualPrefix != null && attrName.equals(actualPrefix + ":nil")) { // Value completion for 'nil' attribute + + if (attrName != null) { + if (actualPrefix != null && attrName.equals(actualPrefix + ":nil")) { // Value completion for 'nil' + // attribute createCompletionItemsForValues(StringUtils.TRUE_FALSE_ARRAY, document, request, response); - } - else if(document.getDocumentElement() != null && document.getDocumentElement().equals(nodeAtOffset)) { // if in the root element - if(attrName.equals("xmlns:xsi")) { + } else if (document.getDocumentElement() != null && document.getDocumentElement().equals(nodeAtOffset)) { // if + // in + // the + // root + // element + if (attrName.equals("xmlns:xsi")) { createSingleCompletionItemForValue(XSI_WEBSITE, document, request, response); } } } } - private static void createSingleCompletionItemForValue(String value, DOMDocument document, ICompletionRequest request, ICompletionResponse response) { + private static void createSingleCompletionItemForValue(String value, DOMDocument document, + ICompletionRequest request, ICompletionResponse response) { createCompletionItemsForValues(Arrays.asList(value), document, request, response); } - private static void createCompletionItemsForValues(Collection enumerationValues, DOMDocument document, ICompletionRequest request, ICompletionResponse response) { + private static void createCompletionItemsForValues(Collection enumerationValues, DOMDocument document, + ICompletionRequest request, ICompletionResponse response) { Range editRange = request.getReplaceRange(); CompletionItem item; for (String option : enumerationValues) { @@ -197,19 +199,18 @@ private static void createCompletionItemsForValues(Collection enumeratio item.setLabel(option); item.setFilterText(insertText); item.setKind(CompletionItemKind.Enum); - item.setTextEdit(new TextEdit(editRange, insertText)); + item.setTextEdit(Either.forLeft(new TextEdit(editRange, insertText))); response.addCompletionItem(item); } } /** - * Checks if a given root has an attribute with the name: - * {@code prefix:suffix}. + * Checks if a given root has an attribute with the name: {@code prefix:suffix}. * If no prefix exists, put the name in {@code suffix} */ private static boolean hasAttribute(DOMElement root, String prefix, String suffix) { return root.getAttributeNode(prefix, suffix) != null; - + } private static boolean hasAttribute(DOMElement root, String name) { @@ -219,33 +220,30 @@ private static boolean hasAttribute(DOMElement root, String name) { public static Hover computeHoverResponse(DOMAttr attribute, IHoverRequest request) { String name = attribute.getName(); - if(!name.startsWith(request.getXMLDocument().getSchemaInstancePrefix() + ":")) { + if (!name.startsWith(request.getXMLDocument().getSchemaInstancePrefix() + ":")) { return null; } DOMDocument document = request.getXMLDocument(); DOMElement root = document.getDocumentElement(); String doc = null; - if(root != null) { - if(root.equals(document.findNodeAt(attribute.getStart()))) { - if(name.endsWith(":schemaLocation")) { + if (root != null) { + if (root.equals(document.findNodeAt(attribute.getStart()))) { + if (name.endsWith(":schemaLocation")) { doc = SCHEMA_LOCATION_DOC; - } - else if(name.endsWith(":noNamespaceSchemaLocation")) { + } else if (name.endsWith(":noNamespaceSchemaLocation")) { doc = NO_NAMESPACE_SCHEMA_LOCATION_DOC; } } } else { return null; } - if(doc == null) { - if(name.endsWith(":nil")) { + if (doc == null) { + if (name.endsWith(":nil")) { doc = NIL_DOC; - } - else if(name.endsWith(":type")) { + } else if (name.endsWith(":type")) { doc = TYPE_DOC; - } - else { + } else { return null; } } @@ -255,7 +253,7 @@ else if(name.endsWith(":type")) { content.setValue(doc); return new Hover(content); } - + public static boolean isXSISchemaLocationAttr(String name, DOMAttr attr) { if (attr != null) { String localName = attr.getLocalName(); diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/AttributeCompletionItem.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/AttributeCompletionItem.java index a4fa211fc3..edc51c0045 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/AttributeCompletionItem.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/AttributeCompletionItem.java @@ -21,6 +21,7 @@ import org.eclipse.lsp4j.InsertTextFormat; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.TextEdit; +import org.eclipse.lsp4j.jsonrpc.messages.Either; public class AttributeCompletionItem extends CompletionItem { @@ -49,7 +50,7 @@ public AttributeCompletionItem(String attrName, boolean canSupportSnippets, Rang canSupportSnippets, 1, true, sharedSettings); attributeContent.append(attributeValue); } - super.setTextEdit(new TextEdit(fullRange, attributeContent.toString())); + super.setTextEdit(Either.forLeft(new TextEdit(fullRange, attributeContent.toString()))); super.setInsertTextFormat(canSupportSnippets ? InsertTextFormat.Snippet : InsertTextFormat.PlainText); } } diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLCompletions.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLCompletions.java index 1166bf714a..69299587f7 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLCompletions.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLCompletions.java @@ -55,6 +55,7 @@ import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.TextEdit; import org.eclipse.lsp4j.jsonrpc.CancelChecker; +import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.w3c.dom.Node; /** @@ -665,7 +666,7 @@ private void collectOpenTagSuggestions(boolean hasOpenBracket, Range replaceRang xml.append(""); } } - item.setTextEdit(new TextEdit(replaceRange, xml.toString())); + item.setTextEdit(Either.forLeft(new TextEdit(replaceRange, xml.toString()))); item.setInsertTextFormat(InsertTextFormat.Snippet); completionResponse.addCompletionItem(item); @@ -727,15 +728,15 @@ private void collectCloseTagSuggestions(Range range, boolean openEndTag, boolean String endIndent = getLineIndent(afterOpenBracket, text); if (startIndent != null && endIndent != null && !startIndent.equals(endIndent)) { String insertText = startIndent + "" : ""; CompletionItem beginProposal = new CompletionItem("#region"); - beginProposal.setTextEdit(new TextEdit(range, text)); + beginProposal.setTextEdit(Either.forLeft(new TextEdit(range, text))); beginProposal.setDocumentation("Insert Folding Region Start"); beginProposal.setFilterText(match.group()); beginProposal.setSortText("za"); @@ -823,7 +824,7 @@ private void collectionRegionProposals(ICompletionRequest request, ICompletionRe response.addCompletionAttribute(beginProposal); CompletionItem endProposal = new CompletionItem("#endregion"); - endProposal.setTextEdit(new TextEdit(range, "")); + endProposal.setTextEdit(Either.forLeft(new TextEdit(range, ""))); endProposal.setDocumentation("Insert Folding Region End"); endProposal.setFilterText(match.group()); endProposal.setSortText("zb"); diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLLanguageService.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLLanguageService.java index a4eb994028..9e436cb523 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLLanguageService.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLLanguageService.java @@ -42,6 +42,7 @@ import org.eclipse.lsp4j.DocumentSymbol; import org.eclipse.lsp4j.FoldingRange; import org.eclipse.lsp4j.Hover; +import org.eclipse.lsp4j.LinkedEditingRanges; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.LocationLink; import org.eclipse.lsp4j.Position; @@ -83,6 +84,7 @@ public void checkCanceled() { private final XMLCodeLens codelens; private final XMLCodeActions codeActions; private final XMLRename rename; + private final XMLLinkedEditing linkedEditing; public XMLLanguageService() { this.formatter = new XMLFormatter(this); @@ -99,6 +101,7 @@ public XMLLanguageService() { this.codelens = new XMLCodeLens(this); this.codeActions = new XMLCodeActions(this); this.rename = new XMLRename(this); + this.linkedEditing = new XMLLinkedEditing(); } @Override @@ -156,8 +159,8 @@ public Hover doHover(DOMDocument xmlDocument, Position position, SharedSettings return hover.doHover(xmlDocument, position, sharedSettings, cancelChecker); } - public List doDiagnostics(DOMDocument xmlDocument, - XMLValidationSettings validationSettings, CancelChecker cancelChecker) { + public List doDiagnostics(DOMDocument xmlDocument, XMLValidationSettings validationSettings, + CancelChecker cancelChecker) { return diagnostics.doDiagnostics(xmlDocument, validationSettings, cancelChecker); } @@ -285,4 +288,8 @@ public Position getMatchingTagPosition(DOMDocument xmlDocument, Position positio return XMLPositionUtility.getMatchingTagPosition(xmlDocument, position); } + public LinkedEditingRanges findLinkedEditingRanges(DOMDocument xmlDocument, Position position, + CancelChecker cancelChecker) { + return linkedEditing.findLinkedEditingRanges(xmlDocument, position, cancelChecker); + } } diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLLinkedEditing.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLLinkedEditing.java new file mode 100644 index 0000000000..50554874d1 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLLinkedEditing.java @@ -0,0 +1,61 @@ +/******************************************************************************* +* Copyright (c) 2021 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lemminx.services; + +import java.util.Arrays; +import java.util.List; +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.dom.DOMElement; +import org.eclipse.lemminx.dom.DOMNode; +import org.eclipse.lemminx.utils.XMLPositionUtility; +import org.eclipse.lsp4j.LinkedEditingRanges; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.jsonrpc.CancelChecker; + +/** + * XML linked editing support. + * + */ +class XMLLinkedEditing { + + private static Logger LOGGER = Logger.getLogger(XMLLinkedEditing.class.getName()); + + public LinkedEditingRanges findLinkedEditingRanges(DOMDocument document, Position position, + CancelChecker cancelChecker) { + try { + int offset = document.offsetAt(position); + DOMNode n = document.findNodeAt(offset); + if (n == null || !n.isElement()) { + return null; + } + DOMElement node = (DOMElement) n; + if (!node.hasEndTag()) { + return null; + } + + if (node.isInStartTag(offset) || node.isInEndTag(offset, true)) { + List ranges = Arrays.asList(XMLPositionUtility.selectStartTagName(node), + XMLPositionUtility.selectEndTagName(node)); + return new LinkedEditingRanges(ranges); + + } + } catch (BadLocationException e) { + LOGGER.log(Level.SEVERE, "In XMLLinkedEditing, position error", e); + } + return null; + } +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/ClientCapabilitiesWrapper.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/ClientCapabilitiesWrapper.java index 7f0b6319ff..34d6503a4a 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/ClientCapabilitiesWrapper.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/ClientCapabilitiesWrapper.java @@ -107,6 +107,10 @@ public boolean isDocumentHighlightDynamicRegistered() { public boolean isDidChangeWatchedFilesRegistered() { return v3Supported && isDynamicRegistrationSupported(capabilities.getWorkspace().getDidChangeWatchedFiles()); } + + public boolean isLinkedEditingRangeDynamicRegistered() { + return v3Supported && isDynamicRegistrationSupported(getTextDocument().getLinkedEditingRange()); + } private boolean isDynamicRegistrationSupported(DynamicRegistrationCapabilities capability) { return capability != null && capability.getDynamicRegistration() != null diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/ServerCapabilitiesConstants.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/ServerCapabilitiesConstants.java index b42ae1af73..5aea56e174 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/ServerCapabilitiesConstants.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/ServerCapabilitiesConstants.java @@ -44,6 +44,7 @@ private ServerCapabilitiesConstants() { public static final String TEXT_DOCUMENT_TYPEDEFINITION = "textDocument/typeDefinition"; public static final String TEXT_DOCUMENT_HOVER = "textDocument/hover"; public static final String TEXT_DOCUMENT_REFERENCES = "textDocument/references"; + public static final String TEXT_DOCUMENT_LINKED_EDITING_RANGE = "textDocument/linkedEditingRange"; public static final String TEXT_DOCUMENT_HIGHLIGHT = "textDocument/documentHighlight"; public static final String WORKSPACE_CHANGE_FOLDERS = "workspace/didChangeWorkspaceFolders"; @@ -72,7 +73,8 @@ private ServerCapabilitiesConstants() { public static final String DOCUMENT_HIGHLIGHT_ID = UUID.randomUUID().toString(); public static final String WORKSPACE_CHANGE_FOLDERS_ID = UUID.randomUUID().toString(); public static final String WORKSPACE_WATCHED_FILES_ID = UUID.randomUUID().toString(); - + public static final String LINKED_EDITING_RANGE_ID = UUID.randomUUID().toString(); + public static final CompletionOptions DEFAULT_COMPLETION_OPTIONS = new CompletionOptions(false, Arrays.asList(".", ":", "<", "\"", "=", "/", "\\", "?", "\'", "&")); public static final TextDocumentSyncKind DEFAULT_SYNC_OPTION = TextDocumentSyncKind.Full; diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/ServerCapabilitiesInitializer.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/ServerCapabilitiesInitializer.java index d54ec6310b..69e1eb010e 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/ServerCapabilitiesInitializer.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/ServerCapabilitiesInitializer.java @@ -56,6 +56,7 @@ public static ServerCapabilities getNonDynamicServerCapabilities(ClientCapabilit serverCapabilities.setDefinitionProvider(!clientCapabilities.isDefinitionDynamicRegistered()); serverCapabilities.setTypeDefinitionProvider(!clientCapabilities.isTypeDefinitionDynamicRegistered()); serverCapabilities.setReferencesProvider(!clientCapabilities.isReferencesDynamicRegistrationSupported()); + serverCapabilities.setLinkedEditingRangeProvider(!clientCapabilities.isLinkedEditingRangeDynamicRegistered()); if (!clientCapabilities.isLinkDynamicRegistrationSupported()) { serverCapabilities.setDocumentLinkProvider(DEFAULT_LINK_OPTIONS); diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/XMLCapabilityManager.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/XMLCapabilityManager.java index a9db905d69..c83b725bb7 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/XMLCapabilityManager.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/XMLCapabilityManager.java @@ -24,6 +24,7 @@ import static org.eclipse.lemminx.settings.capabilities.ServerCapabilitiesConstants.FORMATTING_ID; import static org.eclipse.lemminx.settings.capabilities.ServerCapabilitiesConstants.FORMATTING_RANGE_ID; import static org.eclipse.lemminx.settings.capabilities.ServerCapabilitiesConstants.HOVER_ID; +import static org.eclipse.lemminx.settings.capabilities.ServerCapabilitiesConstants.LINKED_EDITING_RANGE_ID; import static org.eclipse.lemminx.settings.capabilities.ServerCapabilitiesConstants.LINK_ID; import static org.eclipse.lemminx.settings.capabilities.ServerCapabilitiesConstants.REFERENCES_ID; import static org.eclipse.lemminx.settings.capabilities.ServerCapabilitiesConstants.RENAME_ID; @@ -36,6 +37,7 @@ import static org.eclipse.lemminx.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_HIGHLIGHT; import static org.eclipse.lemminx.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_HOVER; import static org.eclipse.lemminx.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_LINK; +import static org.eclipse.lemminx.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_LINKED_EDITING_RANGE; import static org.eclipse.lemminx.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_REFERENCES; import static org.eclipse.lemminx.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_RENAME; import static org.eclipse.lemminx.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_TYPEDEFINITION; @@ -169,6 +171,9 @@ public void initializeCapabilities() { if (this.getClientCapabilities().isReferencesDynamicRegistrationSupported()) { registerCapability(REFERENCES_ID, TEXT_DOCUMENT_REFERENCES); } + if (this.getClientCapabilities().isLinkedEditingRangeDynamicRegistered()) { + registerCapability(LINKED_EDITING_RANGE_ID, TEXT_DOCUMENT_LINKED_EDITING_RANGE); + } if (this.getClientCapabilities().isDidChangeWatchedFilesRegistered()) { registerWatchedFiles(); } 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 e021ad84fe..9983618efd 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 @@ -1091,6 +1091,14 @@ "name": "value" }] }, + { + "name": "org.eclipse.lsp4j.LinkedEditingRanges", + "allDeclaredFields": true, + "methods": [{ + "name": "", + "parameterTypes": [] + }] + }, { "name": "org.eclipse.lsp4j.Location", "allDeclaredFields": true, @@ -1277,22 +1285,6 @@ "parameterTypes": [] }] }, - { - "name": "org.eclipse.lsp4j.SemanticHighlightingCapabilities", - "allDeclaredFields": true, - "methods": [{ - "name": "", - "parameterTypes": [] - }] - }, - { - "name": "org.eclipse.lsp4j.SemanticHighlightingServerCapabilities", - "allDeclaredFields": true, - "methods": [{ - "name": "", - "parameterTypes": [] - }] - }, { "name": "org.eclipse.lsp4j.ServerCapabilities", "allDeclaredFields": true, diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/XMLAssert.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/XMLAssert.java index 8127e00f26..1e845e0e60 100644 --- a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/XMLAssert.java +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/XMLAssert.java @@ -17,6 +17,7 @@ import static org.junit.jupiter.api.Assertions.assertIterableEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.nio.file.Path; @@ -68,6 +69,7 @@ import org.eclipse.lsp4j.DocumentSymbol; import org.eclipse.lsp4j.Hover; import org.eclipse.lsp4j.HoverCapabilities; +import org.eclipse.lsp4j.LinkedEditingRanges; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.LocationLink; import org.eclipse.lsp4j.MarkedString; @@ -237,12 +239,12 @@ public static void assertCompletion(CompletionList completions, CompletionItem e CompletionItem match = getCompletionMatch(matches, expected); if (expected.getTextEdit() != null && match.getTextEdit() != null) { - if (expected.getTextEdit().getNewText() != null) { - assertEquals(expected.getTextEdit().getNewText(), match.getTextEdit().getNewText()); + if (expected.getTextEdit().getLeft().getNewText() != null) { + assertEquals(expected.getTextEdit().getLeft().getNewText(), match.getTextEdit().getLeft().getNewText()); } - Range r = expected.getTextEdit().getRange(); + Range r = expected.getTextEdit().getLeft().getRange(); if (r != null && r.getStart() != null && r.getEnd() != null) { - assertEquals(expected.getTextEdit().getRange(), match.getTextEdit().getRange()); + assertEquals(expected.getTextEdit().getLeft().getRange(), match.getTextEdit().getLeft().getRange()); } } if (expected.getFilterText() != null && match.getFilterText() != null) { @@ -257,7 +259,7 @@ public static void assertCompletion(CompletionList completions, CompletionItem e private static CompletionItem getCompletionMatch(List matches, CompletionItem expected) { for (CompletionItem item : matches) { - if (expected.getTextEdit().getNewText().equals(item.getTextEdit().getNewText())) { + if (expected.getTextEdit().getLeft().getNewText().equals(item.getTextEdit().getLeft().getNewText())) { return item; } } @@ -273,7 +275,7 @@ public static CompletionItem c(String label, TextEdit textEdit, String filterTex CompletionItem item = new CompletionItem(); item.setLabel(label); item.setFilterText(filterText); - item.setTextEdit(textEdit); + item.setTextEdit(Either.forLeft(textEdit)); if (kind == null) { item.setDocumentation(documentation); } else { @@ -286,7 +288,7 @@ public static CompletionItem c(String label, TextEdit textEdit, String filterTex CompletionItem item = new CompletionItem(); item.setLabel(label); item.setFilterText(filterText); - item.setTextEdit(textEdit); + item.setTextEdit(Either.forLeft(textEdit)); return item; } @@ -535,7 +537,8 @@ public static void testCodeActionsFor(String xml, Diagnostic diagnostic, String } public static void testCodeActionsFor(String xml, Diagnostic diagnostic, String catalogPath, - SharedSettings sharedSettings, XMLLanguageService xmlLanguageService, CodeAction... expected) throws BadLocationException { + SharedSettings sharedSettings, XMLLanguageService xmlLanguageService, CodeAction... expected) + throws BadLocationException { int offset = xml.indexOf('|'); Range range = null; @@ -721,7 +724,8 @@ public static void testDocumentLinkFor(String xml, String fileURI, String catalo testDocumentLinkFor(null, xml, fileURI, catalogPath, expected); } - public static void testDocumentLinkFor(XMLLanguageService xmlLanguageService, String xml, String fileURI, String catalogPath, DocumentLink... expected) { + public static void testDocumentLinkFor(XMLLanguageService xmlLanguageService, String xml, String fileURI, + String catalogPath, DocumentLink... expected) { TextDocument document = new TextDocument(xml, fileURI != null ? fileURI : "test.xml"); if (xmlLanguageService == null) { @@ -885,8 +889,8 @@ public static void testDefinitionFor(String value, String fileURI, LocationLink. testDefinitionFor(null, value, fileURI, expected); } - public static void testDefinitionFor(XMLLanguageService xmlLanguageService, String value, String fileURI, LocationLink... expected) - throws BadLocationException { + public static void testDefinitionFor(XMLLanguageService xmlLanguageService, String value, String fileURI, + LocationLink... expected) throws BadLocationException { int offset = value.indexOf('|'); value = value.substring(0, offset) + value.substring(offset + 1); @@ -979,8 +983,8 @@ public static void testReferencesFor(String value, String fileURI, Location... e testReferencesFor(null, value, fileURI, expected); } - public static void testReferencesFor(XMLLanguageService xmlLanguageService, String value, String fileURI, Location... expected) - throws BadLocationException { + public static void testReferencesFor(XMLLanguageService xmlLanguageService, String value, String fileURI, + Location... expected) throws BadLocationException { int offset = value.indexOf('|'); value = value.substring(0, offset) + value.substring(offset + 1); @@ -1025,7 +1029,8 @@ public static void testCodeLensFor(String value, String fileURI, CodeLens... exp testCodeLensFor(value, fileURI, new XMLLanguageService(), expected); } - public static void testCodeLensFor(String value, String fileURI, XMLLanguageService xmlLanguageService, CodeLens... expected) { + public static void testCodeLensFor(String value, String fileURI, XMLLanguageService xmlLanguageService, + CodeLens... expected) { TextDocument document = new TextDocument(value, fileURI != null ? fileURI : "test://test/test.xml"); if (xmlLanguageService == null) { @@ -1111,13 +1116,13 @@ public static DocumentHighlight hl(Range range, DocumentHighlightKind kind) { return new DocumentHighlight(range, kind); } - public static void assertHighlights(String value, int[] expectedMatches, String elementName) throws BadLocationException { + public static void assertHighlights(String value, int[] expectedMatches, String elementName) + throws BadLocationException { assertHighlights(null, value, expectedMatches, elementName); } public static void assertHighlights(XMLLanguageService languageService, String value, int[] expectedMatches, - String elementName) - throws BadLocationException { + String elementName) throws BadLocationException { int offset = value.indexOf("|"); value = value.substring(0, offset) + value.substring(offset + 1); @@ -1161,8 +1166,8 @@ public static void assertFormat(String unformatted, String expected, SharedSetti assertFormat(null, unformatted, expected, sharedSettings, uri, considerRangeFormat); } - public static void assertFormat(XMLLanguageService languageService, String unformatted, String expected, SharedSettings sharedSettings, String uri, - Boolean considerRangeFormat) throws BadLocationException { + public static void assertFormat(XMLLanguageService languageService, String unformatted, String expected, + SharedSettings sharedSettings, String uri, Boolean considerRangeFormat) throws BadLocationException { Range range = null; int rangeStart = considerRangeFormat ? unformatted.indexOf('|') : -1; int rangeEnd = considerRangeFormat ? unformatted.lastIndexOf('|') : -1; @@ -1207,8 +1212,8 @@ public static void assertRename(String value, String newText, List exp assertRename(null, value, newText, expectedEdits); } - public static void assertRename(XMLLanguageService languageService, String value, String newText, List expectedEdits) - throws BadLocationException { + public static void assertRename(XMLLanguageService languageService, String value, String newText, + List expectedEdits) throws BadLocationException { int offset = value.indexOf("|"); value = value.substring(0, offset) + value.substring(offset + 1); @@ -1224,6 +1229,49 @@ public static void assertRename(XMLLanguageService languageService, String value assertArrayEquals(expectedEdits.toArray(), actualEdits.toArray()); } + // ------------------- Linked Editing assert + + public static void testLinkedEditingFor(String xml, LinkedEditingRanges expected) throws BadLocationException { + testLinkedEditingFor(xml, null, expected); + } + + public static void testLinkedEditingFor(String value, String fileURI, LinkedEditingRanges expected) + throws BadLocationException { + int offset = value.indexOf('|'); + value = value.substring(0, offset) + value.substring(offset + 1); + + TextDocument document = new TextDocument(value, fileURI != null ? fileURI : "test://test/test.xml"); + Position position = document.positionAt(offset); + + XMLLanguageService xmlLanguageService = new XMLLanguageService(); + + ContentModelSettings settings = new ContentModelSettings(); + settings.setUseCache(false); + xmlLanguageService.doSave(new SettingsSaveContext(settings)); + + DOMDocument xmlDocument = DOMParser.getInstance().parse(document, + xmlLanguageService.getResolverExtensionManager()); + xmlLanguageService.setDocumentProvider((uri) -> xmlDocument); + + LinkedEditingRanges actual = xmlLanguageService.findLinkedEditingRanges(xmlDocument, position, () -> { + }); + assertLinkedEditing(actual, expected); + } + + public static void assertLinkedEditing(LinkedEditingRanges actual, LinkedEditingRanges expected) { + if (expected == null) { + assertNull(actual); + } else { + assertNotNull(actual); + assertEquals(expected.getWordPattern(), actual.getWordPattern()); + assertEquals(expected.getRanges(), actual.getRanges()); + } + } + + public static LinkedEditingRanges le(Range... ranges) { + return new LinkedEditingRanges(Arrays.asList(ranges)); + } + // ------------------- Generator assert public static void assertGrammarGenerator(String xml, FileContentGeneratorSettings grammarSettings, diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/XMLCompletionTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/XMLCompletionTest.java index 2514751e3f..4b53fd1175 100644 --- a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/XMLCompletionTest.java +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/XMLCompletionTest.java @@ -215,12 +215,12 @@ public void assertOpenStartTagCompletion(String xmlText, int expectedStartTagOff assertEquals(currentTag, completionItem.getLabel()); assertEquals(startWithTagOpen ? "<" + currentTag : currentTag, completionItem.getFilterText()); try { - Range range = completionItem.getTextEdit().getRange(); + Range range = completionItem.getTextEdit().getLeft().getRange(); assertEquals(expectedStartTagOffset, xmlDocument.offsetAt(range.getStart())); } catch (Exception e) { fail("Couldn't get offset at position"); } - assertEquals(currentTextEdit, completionItem.getTextEdit().getNewText()); + assertEquals(currentTextEdit, completionItem.getTextEdit().getLeft().getNewText()); } } diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/XMLLinkedEditingTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/XMLLinkedEditingTest.java new file mode 100644 index 0000000000..e5c5c44b0d --- /dev/null +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/XMLLinkedEditingTest.java @@ -0,0 +1,59 @@ +/******************************************************************************* +* Copyright (c) 2021 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lemminx.services; + +import static org.eclipse.lemminx.XMLAssert.le; +import static org.eclipse.lemminx.XMLAssert.r; +import static org.eclipse.lemminx.XMLAssert.testLinkedEditingFor; + +import org.eclipse.lemminx.commons.BadLocationException; +import org.junit.jupiter.api.Test; + +/** + * Test for XML linked editing. + * + * @author Angelo ZERR + * + */ +public class XMLLinkedEditingTest { + + @Test + public void linkedEditing() throws BadLocationException { + testLinkedEditingFor("|
", null); + testLinkedEditingFor("<|div>", le(r(0, 1, 0, 4), r(0, 7, 0, 10))); + testLinkedEditingFor("", le(r(0, 1, 0, 4), r(0, 7, 0, 10))); + testLinkedEditingFor("", le(r(0, 1, 0, 4), r(0, 7, 0, 10))); + testLinkedEditingFor("", le(r(0, 1, 0, 4), r(0, 7, 0, 10))); + + testLinkedEditingFor("
|
", null); + testLinkedEditingFor("
<|/div>", null); + + testLinkedEditingFor("
", le(r(0, 1, 0, 4), r(0, 7, 0, 10))); + testLinkedEditingFor("
", le(r(0, 1, 0, 4), r(0, 7, 0, 10))); + testLinkedEditingFor("
", le(r(0, 1, 0, 4), r(0, 7, 0, 10))); + testLinkedEditingFor("
", le(r(0, 1, 0, 4), r(0, 7, 0, 10))); + + testLinkedEditingFor("
|", null); + // FIXME + // testLinkedEditingFor("
", null); + // testLinkedEditingFor("
", null); + + testLinkedEditingFor("
", le(r(0, 1, 0, 4), r(0, 8, 0, 11))); + testLinkedEditingFor("
", le(r(0, 1, 0, 4), r(0, 16, 0, 19))); + + // FIXME + // testLinkedEditingFor("<|>", le(r(0, 1, 0, 1), r(0, 4, 0, 4))); + // testLinkedEditingFor("<>
", le(r(0, 1, 0, 1), r(0, 15, 0, + // 15))); + + } +} diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/extensions/HTMLCompletionExtensionsTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/extensions/HTMLCompletionExtensionsTest.java index 6b42418a75..5abb96b0a9 100644 --- a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/extensions/HTMLCompletionExtensionsTest.java +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/services/extensions/HTMLCompletionExtensionsTest.java @@ -161,7 +161,7 @@ public void onTagOpen(ICompletionRequest completionRequest, ICompletionResponse item.setFilterText(completionRequest.getFilterForStartTagName(tag)); item.setKind(CompletionItemKind.Property); item.setDocumentation(Either.forLeft(label)); - item.setTextEdit(new TextEdit(range, "<" + tag + "/>")); + item.setTextEdit(Either.forLeft(new TextEdit(range, "<" + tag + "/>"))); item.setInsertTextFormat(InsertTextFormat.PlainText); completionResponse.addCompletionItem(item); }); @@ -186,7 +186,7 @@ public void onAttributeName(boolean generateValue, ICompletionRequest completion item.setLabel(attribute); item.setKind(CompletionItemKind.Value); String value = generateValue ? "=\"$1\"" : ""; - item.setTextEdit(new TextEdit(replaceRange, attribute + value)); + item.setTextEdit(Either.forLeft(new TextEdit(replaceRange, attribute + value))); item.setInsertTextFormat(InsertTextFormat.Snippet); completionResponse.addCompletionAttribute(item); } @@ -222,7 +222,7 @@ public void onAttributeValue(String valuePrefix, ICompletionRequest completionRe item.setLabel(value); item.setFilterText(insertText); item.setKind(CompletionItemKind.Unit); - item.setTextEdit(new TextEdit(fullRange, insertText)); + item.setTextEdit(Either.forLeft(new TextEdit(fullRange, insertText))); item.setInsertTextFormat(InsertTextFormat.PlainText); completionResponse.addCompletionAttribute(item); } @@ -239,7 +239,7 @@ public void onXMLContent(ICompletionRequest request, ICompletionResponse respons TextEdit edit = new TextEdit(); edit.setNewText("replacement text"); edit.setRange(request.getReplaceRange()); - completion.setTextEdit(edit); + completion.setTextEdit(Either.forLeft(edit)); response.addCompletionItem(completion); } } diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/settings/capabilities/XMLCapabilitiesTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/settings/capabilities/XMLCapabilitiesTest.java index b19e34294f..c901b8c848 100644 --- a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/settings/capabilities/XMLCapabilitiesTest.java +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/settings/capabilities/XMLCapabilitiesTest.java @@ -39,6 +39,7 @@ import org.eclipse.lsp4j.ServerCapabilities; import org.eclipse.lsp4j.TextDocumentClientCapabilities; import org.eclipse.lsp4j.WorkspaceClientCapabilities; +import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.lsp4j.services.LanguageClient; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -48,6 +49,10 @@ */ public class XMLCapabilitiesTest { + private static final Either TRUE = Either.forLeft(true); + + private static final Either FALSE = Either.forLeft(false); + private LanguageClient languageClient = new MockXMLLanguageClient(); private XMLCapabilityManager manager; private ClientCapabilities clientCapabilities; @@ -79,14 +84,14 @@ public void testAllDynamicCapabilities() { ServerCapabilities serverCapabilities = ServerCapabilitiesInitializer .getNonDynamicServerCapabilities(manager.getClientCapabilities(), false); - assertEquals(false, serverCapabilities.getDocumentRangeFormattingProvider()); - assertEquals(false, serverCapabilities.getDocumentFormattingProvider()); - assertEquals(false, serverCapabilities.getDocumentSymbolProvider()); - assertEquals(false, serverCapabilities.getHoverProvider()); - assertEquals(false, serverCapabilities.getDocumentHighlightProvider()); - assertEquals(false, serverCapabilities.getRenameProvider().getLeft()); - assertEquals(false, serverCapabilities.getFoldingRangeProvider().getLeft()); - assertEquals(false, serverCapabilities.getCodeActionProvider().getLeft()); + assertEquals(FALSE, serverCapabilities.getDocumentRangeFormattingProvider()); + assertEquals(FALSE, serverCapabilities.getDocumentFormattingProvider()); + assertEquals(FALSE, serverCapabilities.getDocumentSymbolProvider()); + assertEquals(FALSE, serverCapabilities.getHoverProvider()); + assertEquals(FALSE, serverCapabilities.getDocumentHighlightProvider()); + assertEquals(FALSE, serverCapabilities.getRenameProvider()); + assertEquals(FALSE, serverCapabilities.getFoldingRangeProvider()); + assertEquals(FALSE, serverCapabilities.getCodeActionProvider()); assertEquals(null, serverCapabilities.getCompletionProvider()); assertEquals(null, serverCapabilities.getDocumentLinkProvider()); } @@ -100,14 +105,14 @@ public void testNoDynamicCapabilities() { ServerCapabilities serverCapabilities = ServerCapabilitiesInitializer .getNonDynamicServerCapabilities(manager.getClientCapabilities(), false); - assertEquals(true, serverCapabilities.getDocumentRangeFormattingProvider()); - assertEquals(true, serverCapabilities.getDocumentFormattingProvider()); - assertEquals(true, serverCapabilities.getDocumentSymbolProvider()); - assertEquals(true, serverCapabilities.getHoverProvider()); - assertEquals(true, serverCapabilities.getDocumentHighlightProvider()); - assertEquals(true, serverCapabilities.getRenameProvider().getLeft()); - assertEquals(true, serverCapabilities.getFoldingRangeProvider().getLeft()); - assertEquals(true, serverCapabilities.getCodeActionProvider().getLeft()); + assertEquals(TRUE, serverCapabilities.getDocumentRangeFormattingProvider()); + assertEquals(TRUE, serverCapabilities.getDocumentFormattingProvider()); + assertEquals(TRUE, serverCapabilities.getDocumentSymbolProvider()); + assertEquals(TRUE, serverCapabilities.getHoverProvider()); + assertEquals(TRUE, serverCapabilities.getDocumentHighlightProvider()); + assertEquals(TRUE, serverCapabilities.getRenameProvider()); + assertEquals(TRUE, serverCapabilities.getFoldingRangeProvider()); + assertEquals(TRUE, serverCapabilities.getCodeActionProvider()); assertEquals(DEFAULT_COMPLETION_OPTIONS, serverCapabilities.getCompletionProvider()); assertEquals(DEFAULT_LINK_OPTIONS, serverCapabilities.getDocumentLinkProvider()); } @@ -143,14 +148,14 @@ public void testBothCapabilityTypes() { ServerCapabilities serverCapabilities = ServerCapabilitiesInitializer .getNonDynamicServerCapabilities(manager.getClientCapabilities(), false); - assertEquals(false, serverCapabilities.getDocumentRangeFormattingProvider()); - assertEquals(false, serverCapabilities.getDocumentFormattingProvider()); - assertEquals(false, serverCapabilities.getDocumentSymbolProvider()); - assertEquals(true, serverCapabilities.getHoverProvider()); - assertEquals(true, serverCapabilities.getDocumentHighlightProvider()); - assertEquals(true, serverCapabilities.getRenameProvider().getLeft()); - assertEquals(true, serverCapabilities.getFoldingRangeProvider().getLeft()); - assertEquals(true, serverCapabilities.getCodeActionProvider().getLeft()); + assertEquals(FALSE, serverCapabilities.getDocumentRangeFormattingProvider()); + assertEquals(FALSE, serverCapabilities.getDocumentFormattingProvider()); + assertEquals(FALSE, serverCapabilities.getDocumentSymbolProvider()); + assertEquals(TRUE, serverCapabilities.getHoverProvider()); + assertEquals(TRUE, serverCapabilities.getDocumentHighlightProvider()); + assertEquals(TRUE, serverCapabilities.getRenameProvider()); + assertEquals(TRUE, serverCapabilities.getFoldingRangeProvider()); + assertEquals(TRUE, serverCapabilities.getCodeActionProvider()); assertEquals(null, serverCapabilities.getCompletionProvider()); assertEquals(DEFAULT_LINK_OPTIONS, serverCapabilities.getDocumentLinkProvider()); } @@ -169,8 +174,8 @@ public void testDynamicFormattingWithPreferenceFalse() { ServerCapabilities serverCapabilities = ServerCapabilitiesInitializer .getNonDynamicServerCapabilities(manager.getClientCapabilities(), false); - assertEquals(false, serverCapabilities.getDocumentRangeFormattingProvider()); - assertEquals(false, serverCapabilities.getDocumentFormattingProvider()); + assertEquals(FALSE, serverCapabilities.getDocumentRangeFormattingProvider()); + assertEquals(FALSE, serverCapabilities.getDocumentFormattingProvider()); } @Test @@ -189,8 +194,8 @@ public void testDynamicFormattingWithPreferenceTrue() { ServerCapabilities serverCapabilities = ServerCapabilitiesInitializer .getNonDynamicServerCapabilities(manager.getClientCapabilities(), false); - assertEquals(false, serverCapabilities.getDocumentRangeFormattingProvider()); - assertEquals(false, serverCapabilities.getDocumentFormattingProvider()); + assertEquals(FALSE, serverCapabilities.getDocumentRangeFormattingProvider()); + assertEquals(FALSE, serverCapabilities.getDocumentFormattingProvider()); } private void setAllCapabilities(boolean areAllDynamic) { diff --git a/pom.xml b/pom.xml index 7563ade99e..57da447054 100644 --- a/pom.xml +++ b/pom.xml @@ -4,11 +4,10 @@ lemminx-parent 0.16.1-SNAPSHOT pom - Eclipse LemMinX LemMinX is a XML Language Server Protocol (LSP), and can be used with any editor that supports LSP, to offer an outstanding XML editing experience UTF-8 - 0.9.0 + 0.11.0 5.6.1 https://github.com/eclipse/lemminx