Skip to content

Commit

Permalink
Support End close tag (see
Browse files Browse the repository at this point in the history
  • Loading branch information
angelozerr committed Oct 8, 2018
1 parent fc8fdc0 commit 68c9d4e
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 91 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ public CompletionList doComplete(XMLDocument xmlDocument, Position position, Com

String text = xmlDocument.getText();
if (text.isEmpty()) {
// When XML document is empty, try to collect root element (from file association)
// When XML document is empty, try to collect root element (from file
// association)
collectInsideContent(completionRequest, completionResponse);
return completionResponse;
}
Expand Down Expand Up @@ -178,8 +179,6 @@ public CompletionList doComplete(XMLDocument xmlDocument, Position position, Com
if (offset <= scanner.getTokenEnd()) {
if (currentTag != null && currentTag.length() > 0) {
collectInsideContent(completionRequest, completionResponse);
collectAutoCloseTagSuggestion(scanner.getTokenEnd(), currentTag, completionRequest,
completionResponse);
return completionResponse;
}
}
Expand Down Expand Up @@ -207,8 +206,9 @@ public CompletionList doComplete(XMLDocument xmlDocument, Position position, Com
}
break;
case PrologName:
if(offset <= scanner.getTokenEnd()) {
createPrologCompletionSuggestion(scanner.getTokenEnd(), scanner.getTokenText(), completionRequest, completionResponse);
if (offset <= scanner.getTokenEnd()) {
collectPrologSuggestion(scanner.getTokenEnd(), scanner.getTokenText(), completionRequest,
completionResponse);
return completionResponse;
}
default:
Expand Down Expand Up @@ -355,25 +355,33 @@ private void collectOpenTagSuggestions(boolean hasOpenBracket, Range replaceRang
}
}

private void createPrologCompletionSuggestion(int tagNameEnd, String tag, CompletionRequest request,
CompletionResponse response) {
XMLDocument document = request.getXMLDocument();
CompletionItem item = new CompletionItem();
item.setLabel("<?xml ... ?>");
item.setKind(CompletionItemKind.Property);
item.setFilterText("version=\"1.0\" encoding=\"UTF-8\"?>");
item.setInsertTextFormat(InsertTextFormat.Snippet);
int closingBracketOffset = getOffsetFollowedBy(document.getText(), tagNameEnd, ScannerState.WithinTag, TokenType.StartTagClose);
int end = closingBracketOffset != -1 ? closingBracketOffset - 1 : tagNameEnd;
String closeTag = closingBracketOffset != -1 ? "" : ">";
try {
Range editRange = getReplaceRange(tagNameEnd, end, request);
item.setTextEdit(new TextEdit(editRange, " version=\"1.0\" encoding=\"UTF-8\"?" + closeTag + "$0"));
} catch (BadLocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
response.addCompletionItem(item);
/**
* Collect xml prolog completions.
*
* @param tagNameEnd
* @param tag
* @param request
* @param response
*/
private void collectPrologSuggestion(int tagNameEnd, String tag, CompletionRequest request,
CompletionResponse response) {
XMLDocument document = request.getXMLDocument();
CompletionItem item = new CompletionItem();
item.setLabel("<?xml ... ?>");
item.setKind(CompletionItemKind.Property);
item.setFilterText("version=\"1.0\" encoding=\"UTF-8\"?>");
item.setInsertTextFormat(InsertTextFormat.Snippet);
int closingBracketOffset = getOffsetFollowedBy(document.getText(), tagNameEnd, ScannerState.WithinTag,
TokenType.StartTagClose);
int end = closingBracketOffset != -1 ? closingBracketOffset - 1 : tagNameEnd;
String closeTag = closingBracketOffset != -1 ? "" : ">";
try {
Range editRange = getReplaceRange(tagNameEnd, end, request);
item.setTextEdit(new TextEdit(editRange, " version=\"1.0\" encoding=\"UTF-8\"?" + closeTag + "$0"));
} catch (BadLocationException e) {
LOGGER.log(Level.SEVERE, "While performing getReplaceRange for prolog completion.", e);
}
response.addCompletionItem(item);

}

Expand All @@ -382,36 +390,53 @@ private void collectCloseTagSuggestions(int afterOpenBracket, boolean inOpenTag,
try {
Range range = getReplaceRange(afterOpenBracket, tagNameEnd, completionRequest);
String text = completionRequest.getXMLDocument().getText();
String closeTag = isFollowedBy(text, tagNameEnd, ScannerState.WithinEndTag, TokenType.EndTagClose) ? ""
: ">";
boolean hasCloseTag = isFollowedBy(text, tagNameEnd, ScannerState.WithinEndTag, TokenType.EndTagClose);
collectCloseTagSuggestions(range, false, !hasCloseTag, inOpenTag, completionRequest, completionResponse);
} catch (BadLocationException e) {
LOGGER.log(Level.SEVERE, "While performing Completions the provided offset was a BadLocation", e);
}
}

private void collectCloseTagSuggestions(Range range, boolean openEndTag, boolean closeEndTag, boolean inOpenTag,
CompletionRequest completionRequest, CompletionResponse completionResponse) {
try {
String text = completionRequest.getXMLDocument().getText();
Node curr = completionRequest.getNode();
if (inOpenTag) {
curr = curr.getParent(); // don't suggest the own tag, it's not yet open
}
String closeTag = closeEndTag ? ">" : "";
int afterOpenBracket = completionRequest.getXMLDocument().offsetAt(range.getStart());
if (!openEndTag) {
afterOpenBracket--;
}
int offset = completionRequest.getOffset();
while (curr != null) {
if (curr.isElement()) {
Element element = ((Element) curr);
String tag = element.getTagName();
if (tag != null
&& (!curr.isClosed() || element.hasEndTag() && (element.getEndTagOpenOffset() > offset))) {
if (tag != null && (!element
.isClosed() /* || element.hasEndTag() && (element.getEndTagOpenOffset() > offset) */)) {
CompletionItem item = new CompletionItem();
item.setLabel("/" + tag);
item.setLabel("End with '</" + tag + ">'");
item.setKind(CompletionItemKind.Property);
item.setFilterText("/" + tag + closeTag);
item.setTextEdit(new TextEdit(range, "/" + tag + closeTag));
item.setInsertTextFormat(InsertTextFormat.PlainText);

String startIndent = getLineIndent(curr.getStart(), text);
String endIndent = getLineIndent(afterOpenBracket - 1, text);
String startIndent = getLineIndent(element.getStart(), text);
String endIndent = getLineIndent(afterOpenBracket, text);
if (startIndent != null && endIndent != null && !startIndent.equals(endIndent)) {
String insertText = startIndent + "</" + tag + closeTag;
item.setTextEdit(new TextEdit(getReplaceRange(afterOpenBracket - 1 - endIndent.length(),
offset, completionRequest), insertText));
item.setTextEdit(new TextEdit(
getReplaceRange(afterOpenBracket - endIndent.length(), offset, completionRequest),
insertText));
item.setFilterText(endIndent + "</" + tag + closeTag);
} else {
String openTag = openEndTag ? "<" : "";
String insertText = openTag + "/" + tag + closeTag;
item.setFilterText(insertText);
item.setTextEdit(new TextEdit(range, insertText));
}
completionResponse.addCompletionItem(item);
return;
}
}
curr = curr.getParent();
Expand All @@ -425,29 +450,11 @@ private void collectCloseTagSuggestions(int afterOpenBracket, boolean inOpenTag,
}
}

private void collectAutoCloseTagSuggestion(int tagCloseEnd, String tag, CompletionRequest request,
CompletionResponse response) {
Position pos;
try {
XMLDocument document = request.getXMLDocument();
pos = document.positionAt(tagCloseEnd);
} catch (BadLocationException e) {
LOGGER.log(Level.SEVERE, "While performing Completions the provided offset was a BadLocation", e);
return;
}
CompletionItem item = new CompletionItem();
item.setLabel("</" + tag + ">");
item.setKind(CompletionItemKind.Property);
item.setFilterText("</" + tag + ">");
item.setTextEdit(new TextEdit(new Range(pos, pos), "$0</" + tag + ">"));
item.setInsertTextFormat(InsertTextFormat.Snippet);
response.addCompletionItem(item);
}

private void collectInsideContent(CompletionRequest request, CompletionResponse response) {
Range tagNameRange = request.getXMLDocument().getElementNameRangeAt(request.getOffset());
if (tagNameRange != null) {
collectOpenTagSuggestions(false, tagNameRange, request, response);
collectCloseTagSuggestions(tagNameRange, true, true, false, request, response);
}
// Participant completion on XML content
for (ICompletionParticipant participant : getCompletionParticipants()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
package org.eclipse.lsp4xml.services;

import static org.eclipse.lsp4xml.XMLAssert.c;
import static org.eclipse.lsp4xml.XMLAssert.r;
import static org.eclipse.lsp4xml.XMLAssert.testCompletionFor;
import static org.eclipse.lsp4xml.XMLAssert.testTagCompletion;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.Arrays;
Expand Down Expand Up @@ -49,17 +49,56 @@ public void initializeLanguageService() {
}

@Test
public void successfulEndTagCompletion() {
assertEndTagCompletion("<a>|", 3, "$0</a>");
assertEndTagCompletion("<a><b>|</a>", 6, "$0</b>");
assertEndTagCompletion("<a> <b>|</a>", 9, "$0</b>");
assertEndTagCompletion("<a><b>|", 6, "$0</b>");
public void successfulEndTagCompletion() throws BadLocationException {
testCompletionFor("<a>|", 1, c("End with '</a>'", "</a>", r(0, 3, 0, 3), "</a>"));
testCompletionFor("<a>a|", 1, c("End with '</a>'", "</a>", r(0, 3, 0, 4), "</a>"));
testCompletionFor("<a><|", 1, c("End with '</a>'", "/a>", r(0, 4, 0, 4), "/a>"));
testCompletionFor("<a></|", 1, c("End with '</a>'", "/a>", r(0, 4, 0, 5), "/a>"));

testCompletionFor("<a><b>|</a>", 1, c("End with '</b>'", "</b>", r(0, 6, 0, 6), "</b>"));
testCompletionFor("<a><b><|</a>", 1, c("End with '</b>'", "/b>", r(0, 7, 0, 7), "/b>"));
testCompletionFor("<a><b></|</a>", 1, c("End with '</b>'", "/b>", r(0, 7, 0, 8), "/b>"));

testCompletionFor("<a> <b>|</a>", 1, c("End with '</b>'", "</b>", r(0, 9, 0, 9), "</b>"));
testCompletionFor("<a> <b><|</a>", 1, c("End with '</b>'", "/b>", r(0, 10, 0, 10), "/b>"));
testCompletionFor("<a> <b></|</a>", 1, c("End with '</b>'", "/b>", r(0, 10, 0, 11), "/b>"));

testCompletionFor("<a><b>|", 2, c("End with '</b>'", "</b>", r(0, 6, 0, 6), "</b>"),
c("End with '</a>'", "</a>", r(0, 6, 0, 6), "</a>"));
testCompletionFor("<a><b><|", 2, c("End with '</b>'", "/b>", r(0, 7, 0, 7), "/b>"),
c("End with '</a>'", "/a>", r(0, 7, 0, 7), "/a>"));
testCompletionFor("<a><b></|", 2, c("End with '</b>'", "/b>", r(0, 7, 0, 8), "/b>"),
c("End with '</a>'", "/a>", r(0, 7, 0, 8), "/a>"));
}

@Test
public void successfulEndTagCompletionWithIndent() throws BadLocationException {
testCompletionFor(" <a>\r\n" + //
"|", 3, //
c("End with '</a>'", " </a>", r(1, 0, 1, 0), "</a>"), //
c("#region", "<!-- #region $1-->", r(1, 0, 1, 0), ""), //
c("#endregion", "<!-- #endregion-->", r(1, 0, 1, 0), ""));
testCompletionFor(" <a>\r\n" + //
"<|", 1, //
c("End with '</a>'", " </a>", r(1, 0, 1, 1), "</a>"));
testCompletionFor("<a></|", 1, c("End with '</a>'", "/a>", r(0, 4, 0, 5), "/a>"));

testCompletionFor(" <a>\r\n" + //
" <b>\r\n" + //
"<|", 2, //
c("End with '</b>'", " </b>", r(2, 0, 2, 1), "</b>"), //
c("End with '</a>'", " </a>", r(2, 0, 2, 1), "</a>"));
}

@Test
public void unneededEndTagCompletion() {
assertEndTagCompletion("<a><a>|</a>", 6, "$0</a>");
assertEndTagCompletion("<a><b>|</b></a>", 6, "$0</b>");
public void unneededEndTagCompletion() throws BadLocationException {
testCompletionFor("<a>|</a>", 0);
testCompletionFor("<a><|</a>", 0);
testCompletionFor("<a></|</a>", 0);

testCompletionFor("<a><b>|</b></a>", 0);
testCompletionFor("<a><b><|</b></a>", 0);
testCompletionFor("<a><b></|</b></a>", 0);
}

@Test
Expand Down Expand Up @@ -105,9 +144,11 @@ public void testAutoCloseEnabledDisabled() throws BadLocationException {
}

@Test
public void testAutoCompletionProlog() throws BadLocationException{
testCompletionFor("<?xml|", false, c("<?xml ... ?>"," version=\"1.0\" encoding=\"UTF-8\"?>$0", new Range(new Position(0,5), new Position(0,5)), "version=\"1.0\" encoding=\"UTF-8\"?>"));
testCompletionFor("<?xml|>", true, c("<?xml ... ?>"," version=\"1.0\" encoding=\"UTF-8\"?$0", new Range(new Position(0,5), new Position(0,5)), "version=\"1.0\" encoding=\"UTF-8\"?>"));
public void testAutoCompletionProlog() throws BadLocationException {
testCompletionFor("<?xml|", false, c("<?xml ... ?>", " version=\"1.0\" encoding=\"UTF-8\"?>$0", r(0, 5, 0, 5),
"version=\"1.0\" encoding=\"UTF-8\"?>"));
testCompletionFor("<?xml|>", true, c("<?xml ... ?>", " version=\"1.0\" encoding=\"UTF-8\"?$0", r(0, 5, 0, 5),
"version=\"1.0\" encoding=\"UTF-8\"?>"));
}

// -------------------Tools----------------------------------------------------------
Expand Down Expand Up @@ -153,30 +194,6 @@ public void assertAutoCloseEndTagCompletion(String xmlText, String expectedTextE
assertEquals(expectedTextEdit, completionList);
}

private void assertEndTagCompletion(String xmlText, int expectedEndTagStartOffset, String expectedTextEdit) {

int offset = getOffset(xmlText);
XMLDocument xmlDocument = initializeXMLDocument(xmlText, offset);
CompletionList completionList = initializeCompletion(xmlText, xmlDocument, offset);

if (expectedTextEdit == null) {// Tag is already closed
assertEquals(0, completionList.getItems().size());
} else {
assertTrue(completionList.getItems().size() > 0);
CompletionItem item = completionList.getItems().get(0);
assertEquals(expectedTextEdit.substring(2), item.getLabel());
assertEquals(expectedTextEdit.substring(2), item.getFilterText());

try {
Range range = item.getTextEdit().getRange();
assertEquals(expectedEndTagStartOffset, xmlDocument.offsetAt(range.getStart()));
} catch (Exception e) {
fail("Couldn't get offset at position");
}
assertEquals(expectedTextEdit, item.getTextEdit().getNewText());
}
}

public int getOffset(String xmlText) {
return xmlText.indexOf("|");
}
Expand Down

0 comments on commit 68c9d4e

Please sign in to comment.