-
Notifications
You must be signed in to change notification settings - Fork 91
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixes #1464 Signed-off-by: azerr <[email protected]>
- Loading branch information
1 parent
def27a7
commit 80d6d13
Showing
18 changed files
with
945 additions
and
455 deletions.
There are no files selected for viewing
101 changes: 101 additions & 0 deletions
101
...eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/filepath/FilePathPlugin.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
/******************************************************************************* | ||
* 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 | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Contributors: | ||
* Red Hat Inc. - initial API and implementation | ||
*******************************************************************************/ | ||
package org.eclipse.lemminx.extensions.filepath; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
import org.eclipse.lemminx.dom.DOMDocument; | ||
import org.eclipse.lemminx.extensions.colors.settings.XMLColorsSettings; | ||
import org.eclipse.lemminx.extensions.filepath.participants.FilePathCompletionParticipant; | ||
import org.eclipse.lemminx.extensions.filepath.settings.FilePathExpression; | ||
import org.eclipse.lemminx.extensions.filepath.settings.FilePathMapping; | ||
import org.eclipse.lemminx.extensions.filepath.settings.FilePathSupportSettings; | ||
import org.eclipse.lemminx.services.extensions.IXMLExtension; | ||
import org.eclipse.lemminx.services.extensions.XMLExtensionsRegistry; | ||
import org.eclipse.lemminx.services.extensions.save.ISaveContext; | ||
import org.eclipse.lsp4j.InitializeParams; | ||
|
||
/** | ||
* FilePathPlugin | ||
*/ | ||
public class FilePathPlugin implements IXMLExtension { | ||
|
||
private final FilePathCompletionParticipant completionParticipant; | ||
private FilePathSupportSettings filePathsSettings; | ||
|
||
public FilePathPlugin() { | ||
completionParticipant = new FilePathCompletionParticipant(this); | ||
} | ||
|
||
@Override | ||
public void doSave(ISaveContext context) { | ||
if (context.getType() != ISaveContext.SaveContextType.DOCUMENT) { | ||
// Settings | ||
updateSettings(context); | ||
} | ||
} | ||
|
||
private void updateSettings(ISaveContext saveContext) { | ||
Object initializationOptionsSettings = saveContext.getSettings(); | ||
FilePathSupportSettings settings = FilePathSupportSettings | ||
.getFilePathsSettings(initializationOptionsSettings); | ||
updateSettings(settings, saveContext); | ||
} | ||
|
||
private void updateSettings(FilePathSupportSettings settings, ISaveContext context) { | ||
this.filePathsSettings = settings; | ||
} | ||
|
||
@Override | ||
public void start(InitializeParams params, XMLExtensionsRegistry registry) { | ||
registry.registerCompletionParticipant(completionParticipant); | ||
} | ||
|
||
@Override | ||
public void stop(XMLExtensionsRegistry registry) { | ||
registry.unregisterCompletionParticipant(completionParticipant); | ||
} | ||
|
||
public FilePathSupportSettings getFilePathsSettings() { | ||
return filePathsSettings; | ||
} | ||
|
||
/** | ||
* Return the list of {@link FilePathExpression} for the given document and an | ||
* empty list otherwise. | ||
* | ||
* @param xmlDocument the DOM document | ||
* | ||
* @return the list of {@link FilePathExpression} for the given document and an | ||
* empty list otherwise. | ||
*/ | ||
public List<FilePathExpression> findFilePathExpression(DOMDocument xmlDocument) { | ||
FilePathSupportSettings settings = getFilePathsSettings(); | ||
if (settings == null) { | ||
return Collections.emptyList(); | ||
} | ||
|
||
List<FilePathMapping> filePathsDef = settings.getFilePathMappings(); | ||
if (filePathsDef == null) { | ||
return Collections.emptyList(); | ||
} | ||
List<FilePathExpression> expressions = new ArrayList<>(); | ||
for (FilePathMapping filePaths : filePathsDef) { | ||
if (filePaths.matches(xmlDocument.getDocumentURI())) { | ||
expressions.addAll(filePaths.getExpressions()); | ||
} | ||
} | ||
return expressions; | ||
} | ||
} |
193 changes: 193 additions & 0 deletions
193
...a/org/eclipse/lemminx/extensions/filepath/participants/FilePathCompletionParticipant.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
/******************************************************************************* | ||
* Copyright (c) 2023 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.extensions.filepath.participants; | ||
|
||
import static org.eclipse.lemminx.utils.platform.Platform.isWindows; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.nio.file.DirectoryStream; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.util.List; | ||
import java.util.logging.Level; | ||
import java.util.logging.Logger; | ||
|
||
import org.eclipse.lemminx.dom.DOMAttr; | ||
import org.eclipse.lemminx.dom.DOMDocument; | ||
import org.eclipse.lemminx.dom.DOMElement; | ||
import org.eclipse.lemminx.dom.DOMNode; | ||
import org.eclipse.lemminx.dom.DOMRange; | ||
import org.eclipse.lemminx.dom.DOMText; | ||
import org.eclipse.lemminx.dom.DTDDeclParameter; | ||
import org.eclipse.lemminx.extensions.filepath.FilePathPlugin; | ||
import org.eclipse.lemminx.extensions.filepath.settings.FilePathExpression; | ||
import org.eclipse.lemminx.services.extensions.completion.CompletionParticipantAdapter; | ||
import org.eclipse.lemminx.services.extensions.completion.ICompletionRequest; | ||
import org.eclipse.lemminx.services.extensions.completion.ICompletionResponse; | ||
import org.eclipse.lemminx.utils.CompletionSortTextHelper; | ||
import org.eclipse.lemminx.utils.FilesUtils; | ||
import org.eclipse.lemminx.utils.XMLPositionUtility; | ||
import org.eclipse.lsp4j.CompletionItem; | ||
import org.eclipse.lsp4j.CompletionItemKind; | ||
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: | ||
* | ||
* <ul> | ||
* <li>attribute value: | ||
* | ||
* <pre> | ||
* <item path="file:///C:/folder" /> | ||
* <item path="file:///C:/folder file:///C:/file.txt" /> | ||
* <item path="/folder" /> | ||
* </pre> | ||
* | ||
* </li> | ||
* <li>DTD DOCTYPE SYSTEM | ||
* | ||
* <pre> | ||
* <!DOCTYPE parent SYSTEM "file.dtd"> | ||
* </pre> | ||
* | ||
* </li> | ||
* | ||
* </ul> | ||
* | ||
* <p> | ||
* | ||
* </p> | ||
*/ | ||
public class FilePathCompletionParticipant extends CompletionParticipantAdapter { | ||
|
||
private static final Logger LOGGER = Logger.getLogger(FilePathCompletionParticipant.class.getName()); | ||
|
||
private final FilePathPlugin filePathPlugin; | ||
|
||
public FilePathCompletionParticipant(FilePathPlugin filePathPlugin) { | ||
this.filePathPlugin = filePathPlugin; | ||
} | ||
|
||
@Override | ||
public void onAttributeValue(String value, ICompletionRequest request, ICompletionResponse response, | ||
CancelChecker cancelChecker) throws Exception { | ||
// File path completion on attribute value | ||
List<FilePathExpression> expressions = filePathPlugin.findFilePathExpression(request.getXMLDocument()); | ||
if (expressions.isEmpty()) { | ||
return; | ||
} | ||
DOMNode node = request.getNode(); | ||
DOMAttr attr = node.findAttrAt(request.getOffset()); | ||
DOMDocument xmlDocument = request.getXMLDocument(); | ||
for (FilePathExpression expression : expressions) { | ||
if (expression.match(attr)) { | ||
DOMRange attrValueRange = attr.getNodeAttrValue(); | ||
addFileCompletionItems(xmlDocument, attrValueRange.getStart() + 1 /* increment to be after the quote */, | ||
attrValueRange.getEnd() - 1, request.getOffset(), expression.getSeparator(), | ||
response); | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public void onXMLContent(ICompletionRequest request, ICompletionResponse response, CancelChecker cancelChecker) | ||
throws Exception { | ||
// File path completion on text node | ||
List<FilePathExpression> expressions = filePathPlugin.findFilePathExpression(request.getXMLDocument()); | ||
if (expressions.isEmpty()) { | ||
return; | ||
} | ||
DOMText textNode = findTextNode(request.getNode(), request.getOffset()); | ||
if (textNode == null) { | ||
return; | ||
} | ||
DOMDocument xmlDocument = request.getXMLDocument(); | ||
for (FilePathExpression expression : expressions) { | ||
if (expression.match(textNode)) { | ||
DOMRange textRange = textNode; | ||
addFileCompletionItems(xmlDocument, textRange.getStart(), textRange.getEnd(), request.getOffset(), | ||
expression.getSeparator(), | ||
response); | ||
} | ||
} | ||
} | ||
|
||
private static DOMText findTextNode(DOMNode node, int offset) { | ||
if (node == null || node.isText()) { | ||
return (DOMText) node; | ||
} | ||
if (node.isElement()) { | ||
DOMText text = ((DOMElement) node).findTextAt(offset); | ||
if (text != null) { | ||
return text; | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
@Override | ||
public void onDTDSystemId(String value, ICompletionRequest request, ICompletionResponse response, | ||
CancelChecker cancelChecker) throws Exception { | ||
// File path completion on DTD DOCTYPE SYSTEM | ||
DOMDocument xmlDocument = request.getXMLDocument(); | ||
DTDDeclParameter systemId = xmlDocument.getDoctype().getSystemIdNode(); | ||
addFileCompletionItems(xmlDocument, systemId.getStart() + 1 /* increment to be after the quote */, | ||
systemId.getEnd() - 1, request.getOffset(), null, response); | ||
} | ||
|
||
private static void addFileCompletionItems(DOMDocument xmlDocument, int startOffset, int endOffset, | ||
int completionOffset, | ||
Character separator, ICompletionResponse response) | ||
throws Exception { | ||
FilePathCompletionResult result = FilePathCompletionResult.create(xmlDocument.getText(), | ||
xmlDocument.getDocumentURI(), startOffset, endOffset, completionOffset, separator); | ||
Path baseDir = result.getBaseDir(); | ||
if (baseDir == null) { | ||
return; | ||
} | ||
String slash = ""; | ||
Range replaceRange = XMLPositionUtility.createRange(result.getStart(), result.getEnd(), xmlDocument); | ||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(baseDir)) { | ||
for (Path entry : stream) { | ||
createFilePathCompletionItem(entry.toFile(), replaceRange, response, slash); | ||
} | ||
} catch (IOException e) { | ||
LOGGER.log(Level.SEVERE, "Error while getting files/directories", e); | ||
} | ||
} | ||
|
||
private static void createFilePathCompletionItem(File f, Range replaceRange, ICompletionResponse response, | ||
String slash) { | ||
CompletionItem item = new CompletionItem(); | ||
String fName = FilesUtils.encodePath(f.getName()); | ||
if (isWindows && fName.isEmpty()) { // Edge case for Windows drive letter | ||
fName = f.getPath(); | ||
fName = fName.substring(0, fName.length() - 1); | ||
} | ||
String insertText; | ||
insertText = slash + fName; | ||
item.setLabel(insertText); | ||
|
||
CompletionItemKind kind = f.isDirectory() ? CompletionItemKind.Folder : CompletionItemKind.File; | ||
item.setKind(kind); | ||
|
||
item.setSortText(CompletionSortTextHelper.getSortText(kind)); | ||
item.setFilterText(insertText); | ||
item.setTextEdit(Either.forLeft(new TextEdit(replaceRange, insertText))); | ||
response.addCompletionItem(item); | ||
} | ||
} |
Oops, something went wrong.