Skip to content

Commit

Permalink
Implement file path completion for DTD systemId
Browse files Browse the repository at this point in the history
Signed-off-by: David Kwon <dakwon@redhat.com>
  • Loading branch information
dkwon17 authored and angelozerr committed Jun 18, 2020
1 parent 981cddd commit f8a0ab1
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 8 deletions.
Expand Up @@ -47,14 +47,25 @@ public class FilePathCompletionParticipant extends CompletionParticipantAdapter
@Override
public void onAttributeValue(String valuePrefix,
ICompletionRequest request, ICompletionResponse response) throws Exception {
addCompletionItems(valuePrefix, request, response);
}

@Override
public void onDTDSystemId(String valuePrefix,
ICompletionRequest request, ICompletionResponse response) throws Exception {
addCompletionItems(valuePrefix, request, response);
}

private void addCompletionItems(String valuePrefix,
ICompletionRequest request, ICompletionResponse response) throws Exception {

DOMDocument xmlDocument = request.getXMLDocument();
String text = xmlDocument.getText();
Range fullRange = request.getReplaceRange();

// Get full attribute value range
int documentStartOffset = xmlDocument.offsetAt(fullRange.getStart());

String fullAttributeValue = valuePrefix;
if (isEmpty(fullAttributeValue)) {
return;
Expand All @@ -64,7 +75,7 @@ public void onAttributeValue(String valuePrefix,
int completionOffset = request.getOffset(); // offset after the typed character
int parsedAttributeStartOffset = StringUtils.getOffsetAfterWhitespace(fullAttributeValue, completionOffset - documentStartOffset) + documentStartOffset; // first character of URI
String attributePath = text.substring(parsedAttributeStartOffset, completionOffset);

Position startValue = xmlDocument.positionAt(parsedAttributeStartOffset);
Position endValue = xmlDocument.positionAt(completionOffset);
fullRange = new Range(startValue, endValue);
Expand Down Expand Up @@ -96,14 +107,14 @@ public void onAttributeValue(String valuePrefix,
return;
}
}

if(isWindows) {
osSpecificAttributePath = convertToWindowsPath(osSpecificAttributePath);
}
else if("\\".equals(slashInAttribute)) { // Backslash used in Unix
osSpecificAttributePath = osSpecificAttributePath.replace("\\", "/");
}

// Get the normalized URI string from the parent directory file if necessary
String workingDirectory = null; // The OS specific path for a working directory

Expand All @@ -129,10 +140,10 @@ else if("\\".equals(slashInAttribute)) { // Backslash used in Unix
}
}
}

//Try to get a correctly formatted path from the given values
Path validAttributeValuePath = getNormalizedPath(workingDirectory, osSpecificAttributePath);

if(validAttributeValuePath == null) {
return;
}
Expand Down
Expand Up @@ -167,6 +167,9 @@ public static void computeValueCompletionResponses(ICompletionRequest request,

String actualPrefix = document.getSchemaInstancePrefix();
DOMAttr attrAtOffset = nodeAtOffset.findAttrAt(offset);
if (attrAtOffset == null) {
return;
}
String attrName = attrAtOffset.getName();

if(attrName != null) {
Expand Down
Expand Up @@ -31,6 +31,7 @@
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.dom.DOMElement;
import org.eclipse.lemminx.dom.DOMNode;
import org.eclipse.lemminx.dom.DTDDeclParameter;
import org.eclipse.lemminx.dom.parser.Scanner;
import org.eclipse.lemminx.dom.parser.ScannerState;
import org.eclipse.lemminx.dom.parser.TokenType;
Expand Down Expand Up @@ -125,6 +126,24 @@ public CompletionList doComplete(DOMDocument xmlDocument, Position position, Sha
return completionResponse;
}
break;
case DTDStartDoctypeTag:

DTDDeclParameter systemId = xmlDocument.getDoctype().getSystemIdNode();
if (systemId == null) {
break;
}

if (DOMNode.isIncluded(systemId, offset)) {
/**
* Completion invoked within systemId parameter
* ie, completion offset is at | like so:
* <!DOCTYPE foo SYSTEM "./|">
*/
collectDTDSystemIdSuggestions(systemId.getStart(), systemId.getEnd(),
completionRequest, completionResponse);
return completionResponse;
}
break;
case AttributeValue:
if (scanner.getTokenOffset() <= offset && offset <= scanner.getTokenEnd()) {
collectAttributeValueSuggestions(scanner.getTokenOffset(), scanner.getTokenEnd(),
Expand Down Expand Up @@ -830,7 +849,7 @@ private void collectAttributeValueSuggestions(int valueStart, int valueEnd, Comp
int valueContentStart = valueStart + 1;
int valueContentEnd = valueEnd;
// valueEnd points to the char after quote, which encloses the replace range
if (valueEnd > valueStart && text.charAt(valueEnd - 1) == text.charAt(valueStart)) {
if (text.charAt(valueEnd - 1) == text.charAt(valueStart)) {
valueContentEnd--;
}
valuePrefix = offset >= valueContentStart && offset <= valueContentEnd
Expand Down Expand Up @@ -862,6 +881,41 @@ private void collectAttributeValueSuggestions(int valueStart, int valueEnd, Comp
}
}

/**
* Collect completion items for DTD systemId
*
* @param valueStart the start offset of the systemId value, including quote
* @param valueEnd the end offset of the systemId value, including quote
* @param completionRequest the completion request
* @param completionResponse the completion response
*/
private void collectDTDSystemIdSuggestions(int valueStart, int valueEnd, CompletionRequest completionRequest,
CompletionResponse completionResponse) {
int offset = completionRequest.getOffset();
String text = completionRequest.getXMLDocument().getText();
int valueContentStart = valueStart + 1;
int valueContentEnd = valueEnd - 1;
String valuePrefix = offset >= valueContentStart && offset <= valueContentEnd
? text.substring(valueContentStart, offset)
: "";
Collection<ICompletionParticipant> completionParticipants = getCompletionParticipants();

if (completionParticipants.size() > 0) {
try {
Range replaceRange = getReplaceRange(valueContentStart, valueContentEnd, completionRequest);
completionRequest.setReplaceRange(replaceRange);
for (ICompletionParticipant participant : completionParticipants) {
participant.onDTDSystemId(valuePrefix, completionRequest, completionResponse);
}
} catch (BadLocationException e) {
LOGGER.log(Level.SEVERE,
"While performing Completions, getReplaceRange() was given a bad Offset location", e);
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "While performing ICompletionParticipant#onDTDSystemId", e);
}
}
}

private static int scanNextForEndPos(int offset, Scanner scanner, TokenType nextToken) {
if (offset == scanner.getTokenEnd()) {
TokenType token = scanner.scan();
Expand Down
Expand Up @@ -41,4 +41,10 @@ public void onAttributeValue(String valuePrefix, ICompletionRequest request, ICo
// Do nothing
}

@Override
public void onDTDSystemId(String valuePrefix, ICompletionRequest request, ICompletionResponse response)
throws Exception {
// Do nothing
}

}
Expand Up @@ -25,7 +25,29 @@ public interface ICompletionParticipant {
void onAttributeName(boolean generateValue, ICompletionRequest request, ICompletionResponse response)
throws Exception;

/**
* Collects and stores attribute value completion items within the provided completion
* response <code>response</code>
*
* @param valuePrefix the attribute value before the offset in which completion was invoked
* @param request the completion request
* @param response the completion response
* @throws Exception
*/
void onAttributeValue(String valuePrefix, ICompletionRequest request, ICompletionResponse response)
throws Exception;


/**
* Collects and stores systemId completion items within the provided completion
* response <code>response</code>
*
* @param valuePrefix the systemId value before the offset in which completion was invoked
* @param request the completion request
* @param response the completion response
* @throws Exception
*/
void onDTDSystemId(String valuePrefix, ICompletionRequest request, ICompletionResponse response)
throws Exception;

}
Expand Up @@ -227,6 +227,61 @@ public void testFilePathCompletionNotValue() throws BadLocationException {
testCompletionFor(xml, 0);
}

@Test
public void testFilePathCompletionDTD() throws BadLocationException {
String xml = "<!DOCTYPE foo SYSTEM \"./|\">";
CompletionItem[] items = getCompletionItemList("/", 0, 23, 24, "folderA", "folderB", "NestedA");
testCompletionFor(xml, items);
}

@Test
public void testFilePathCompletionDTDBackSlash() throws BadLocationException {

String xml = "<!DOCTYPE foo SYSTEM \".\\|\">";
CompletionItem[] items = getCompletionItemList("\\", 0, 23, 24, "folderA", "folderB", "NestedA");
testCompletionFor(xml, items);
}

@Test
public void testFilePathCompletionDTDFolderA() throws BadLocationException {
String xml = "<!DOCTYPE foo SYSTEM \"./folderA/|\">";
CompletionItem[] items = getCompletionItemList("/", 0, 31, 32, "xsdA1.xsd", "xsdA2.xsd");
testCompletionFor(xml, items);
}

@Test
public void testFilePathCompletionDTDFolderABackSlash() throws BadLocationException {
String xml = "<!DOCTYPE foo SYSTEM \".\\folderA\\|\">";
CompletionItem[] items = getCompletionItemList("\\", 0, 31, 32, "xsdA1.xsd", "xsdA2.xsd");
testCompletionFor(xml, items);
}

@Test
public void testFilePathCompletionDTDFolderB() throws BadLocationException {
String xml = "<!DOCTYPE foo SYSTEM \"folderB/|\">";
CompletionItem[] items = getCompletionItemList("/", 0, 29, 30, "xsdB1.xsd", "xmlB1.xml");
testCompletionFor(xml, 2, items);
}

@Test
public void testFilePathCompletionDTDFolderBBackSlash() throws BadLocationException {
String xml = "<!DOCTYPE foo SYSTEM \"folderB\\|\">";
CompletionItem[] items = getCompletionItemList("\\", 0, 29, 30, "xsdB1.xsd", "xmlB1.xml");
testCompletionFor(xml, 2, items);
}

@Test
public void testFilePathNoCompletion() throws BadLocationException {
String xml = "<!DOCTYPE foo SYSTEM \"|\">";
testCompletionFor(xml, 0);
}

@Test
public void testFilePathNoCompletionMissingSystemId() throws BadLocationException {
String xml = "<!DOCTYPE foo \"./|\">";
testCompletionFor(xml, 0);
}

private void testCompletionFor(String xml, CompletionItem... expectedItems) throws BadLocationException {
testCompletionFor(xml, null, expectedItems);
}
Expand Down

0 comments on commit f8a0ab1

Please sign in to comment.