Skip to content

Commit

Permalink
Autocompletion for directory/file based properties eclipse#292
Browse files Browse the repository at this point in the history
Signed-off-by: Victor Rubezhny <vrubezhny@redhat.com>
  • Loading branch information
vrubezhny committed Jun 22, 2022
1 parent 66973ca commit c14bb4f
Show file tree
Hide file tree
Showing 8 changed files with 348 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,19 @@ private DOMConstants() {
public static final String CLASSIFIER_ELT = "classifier";
public static final String EXCLUSION_ELT = "exclusion";
public static final String EXCLUSIONS_ELT = "exclusions";

public static final String TARGET_PATH_ELT = "targetPath";
public static final String DIRECTORY_ELT = "directory";
public static final String SOURCE_DIRECTORY_ELT = "sourceDirectory";
public static final String SCRIPT_SOURCE_DIRECTORY_ELT = "scriptSourceDirectory";
public static final String TEST_SOURCE_DIRECTORY_ELT = "testSourceDirectory";
public static final String OUTPUT_DIRECTORY_ELT = "outputDirectory";
public static final String TEST_OUTPUT_DIRECTORY_ELT = "testOutputDirectory";

public static final String FILTERS_ELT = "filters";
public static final String FILTER_ELT = "filter";
public static final String SYSTEM_PATH_ELT = "systemPath";
public static final String FILE_ELT = "file";
public static final String EXISTS_ELT = "exists";
public static final String MISSING_ELT = "missing";
}
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,26 @@ public void onXMLContent(ICompletionRequest request, ICompletionResponse respons
case MODULE_ELT:
collectSubModuleCompletion(request).forEach(response::addCompletionItem);
break;
case TARGET_PATH_ELT:
case DIRECTORY_ELT:
case SOURCE_DIRECTORY_ELT:
case SCRIPT_SOURCE_DIRECTORY_ELT:
case TEST_SOURCE_DIRECTORY_ELT:
case OUTPUT_DIRECTORY_ELT:
case TEST_OUTPUT_DIRECTORY_ELT:
collectRelativeDirectoryPathCompletion(request).forEach(response::addCompletionItem);
break;
case FILTER_ELT:
if (FILTERS_ELT.equals(grandParent.getLocalName())) {
collectRelativeFilterPathCompletion(request).forEach(response::addCompletionItem);
}
break;
case EXISTS_ELT:
case MISSING_ELT:
if (FILE_ELT.equals(grandParent.getLocalName())) {
collectRelativeAnyPathCompletion(request).forEach(response::addCompletionItem);
}
break;
case RELATIVE_PATH_ELT:
collectRelativePathCompletion(request).forEach(response::addCompletionItem);
break;
Expand Down Expand Up @@ -737,6 +757,122 @@ private Collection<CompletionItem> collectRelativePathCompletion(ICompletionRequ
).map(file -> toFileCompletionItem(file, docFolder, request)).collect(Collectors.toList());
}

private Collection<CompletionItem> collectRelativeAnyPathCompletion(ICompletionRequest request) {
DOMDocument doc = request.getXMLDocument();
File docFile = new File(URI.create(doc.getTextDocument().getUri()));
File docFolder = docFile.getParentFile();
String prefix = request.getNode().getNodeValue() != null ? request.getNode().getNodeValue() : "";
File prefixFile = new File(docFolder, prefix);
List<File> files = new ArrayList<>();
if (prefix.isEmpty()) {
files.add(docFolder.getParentFile());
} else {
try {
prefixFile = prefixFile.getCanonicalFile();
} catch (IOException e) {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
}
if (!prefix.endsWith("/")) {
final File thePrefixFile = prefixFile;
files.addAll(Arrays.asList(prefixFile.getParentFile()
.listFiles(file -> file.getName().startsWith(thePrefixFile.getName()))));
}
}
if (prefixFile.isDirectory()) {
files.addAll(Arrays.asList(prefixFile.listFiles()));
}
return files.stream().filter(file -> file.isFile() || file.isDirectory())
.sorted(Comparator.comparing(File::isFile) // files before folders
.thenComparing(
file -> (file.isFile() && docFile.toPath().startsWith(file.getParentFile().toPath()))
|| (file.isDirectory() && file.equals(docFolder.getParentFile()))) // `files before
.thenComparing(file -> file.getParentFile().getName().contains(PARENT_ELT)) // folders
// containing
// "parent"
// before...
.thenComparing(file -> file.getParentFile().getParentFile().equals(docFolder.getParentFile())) // siblings
// before...
.reversed().thenComparing(Function.identity())// other folders and files
).map(file -> toFileCompletionItem(file, docFolder, request)).collect(Collectors.toList());
}

private Collection<CompletionItem> collectRelativeDirectoryPathCompletion(ICompletionRequest request) {
DOMDocument doc = request.getXMLDocument();
File docFile = new File(URI.create(doc.getTextDocument().getUri()));
File docFolder = docFile.getParentFile();
String prefix = request.getNode().getNodeValue() != null ? request.getNode().getNodeValue() : "";
File prefixFile = new File(docFolder, prefix);
List<File> files = new ArrayList<>();
if (prefix.isEmpty()) {
files.add(docFolder.getParentFile());
} else {
try {
prefixFile = prefixFile.getCanonicalFile();
} catch (IOException e) {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
}
if (!prefix.endsWith("/")) {
final File thePrefixFile = prefixFile;
files.addAll(Arrays.asList(prefixFile.getParentFile()
.listFiles(file -> file.getName().startsWith(thePrefixFile.getName()))));
}
}
if (prefixFile.isDirectory()) {
files.addAll(Arrays.asList(prefixFile.listFiles()));
}
return files.stream().filter(file -> file.isDirectory())
.filter( file -> !file.equals(docFolder))
.sorted(Comparator.comparing(File::isDirectory) // only folders
.thenComparing(file -> (file.isDirectory() && file.equals(docFolder.getParentFile())))
.thenComparing(file -> file.getParentFile().getName().contains(PARENT_ELT)) // folders containing
// "parent" before...
.thenComparing(file -> file.getParentFile().getParentFile().equals(docFolder.getParentFile())) // siblings before...
.reversed().thenComparing(Function.identity())// other folders and files
).map(file -> toFileCompletionItem(file, docFolder, request)).collect(Collectors.toList());
}

private List<File> collectRelativePropertiesFiles(File parent) {
List<File> result = new ArrayList<>();
List<File> parentFiles = Arrays.asList(parent.listFiles());

parentFiles.stream().filter(file -> (file.isFile() && file.getName().endsWith(".properties")))
.forEach(file -> result.add(file));
parentFiles.stream().filter(file -> (file.isDirectory()))
.forEach(file -> result.addAll(collectRelativePropertiesFiles(file)));
return result;
}

private Collection<CompletionItem> collectRelativeFilterPathCompletion(ICompletionRequest request) {
DOMDocument doc = request.getXMLDocument();
File docFile = new File(URI.create(doc.getTextDocument().getUri()));
File docFolder = docFile.getParentFile();
String prefix = request.getNode().getNodeValue() != null ? request.getNode().getNodeValue() : "";
File prefixFile = new File(docFolder, prefix);
List<File> files = new ArrayList<>();
if (!prefix.isEmpty()) {
try {
prefixFile = prefixFile.getCanonicalFile();
} catch (IOException e) {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
}
if (!prefix.endsWith("/")) {
final File thePrefixFile = prefixFile;
files.addAll(Arrays.asList(prefixFile.getParentFile()
.listFiles(file -> (file.getName().startsWith(thePrefixFile.getName())
&& file.getName().endsWith(".properties")))));
}
}
if (prefixFile.isDirectory()) {
files.addAll(collectRelativePropertiesFiles(prefixFile));
}
return files.stream()
.sorted(Comparator.comparing(File::isFile) // pom files before folders
.thenComparing(
file -> (file.isFile() && docFile.toPath().startsWith(file.getParentFile().toPath())))
.reversed().thenComparing(Function.identity())// other folders and files
).map(file -> toFileCompletionItem(file, docFolder, request)).collect(Collectors.toList());
}

private CompletionItem toFileCompletionItem(File file, File referenceFolder, ICompletionRequest request) {
CompletionItem res = new CompletionItem();
Path path = referenceFolder.toPath().relativize(file.toPath());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/*******************************************************************************
* Copyright (c) 2022 Red Hat Inc. and others.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.lemminx.extensions.maven.participants.completion;

import static org.eclipse.lemminx.extensions.maven.utils.MavenLemminxTestsUtils.createDOMDocument;
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutionException;

import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.extensions.maven.NoMavenCentralExtension;
import org.eclipse.lemminx.services.XMLLanguageService;
import org.eclipse.lemminx.settings.SharedSettings;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.Position;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

@ExtendWith(NoMavenCentralExtension.class)
public class PathWebResourcesTest {

private XMLLanguageService languageService;

@BeforeEach
public void setUp() throws IOException {
languageService = new XMLLanguageService();
}

@AfterEach
public void tearDown() throws InterruptedException, ExecutionException {
languageService.dispose();
languageService = null;
}

// public static final String DIRECTORY_ELT = "directory";
@Test
public void testWebResourcesDirectory() throws Exception {
DOMDocument document = createDOMDocument("/local-path/pom-webresource.xml", languageService);
List<CompletionItem> doComplete = languageService.doComplete(document, new Position(0, 11), new SharedSettings()).getItems();

// parent folder, then direct children
assertEquals("..", doComplete.get(0).getLabel());
assertEquals("child-parent", doComplete.get(1).getLabel());
assertEquals("folder1", doComplete.get(2).getLabel());
assertEquals("folder2", doComplete.get(3).getLabel());
}

// public static final String TARGET_PATH_ELT = "targetPath";
@Test
public void testWebResourcesTargetPath() throws Exception {
DOMDocument document = createDOMDocument("/local-path/pom-webresource.xml", languageService);
List<CompletionItem> doComplete = languageService.doComplete(document, new Position(0, 11), new SharedSettings()).getItems();

// parent folder, then direct children
assertEquals("..", doComplete.get(0).getLabel());
assertEquals("child-parent", doComplete.get(1).getLabel());
assertEquals("folder1", doComplete.get(2).getLabel());
assertEquals("folder2", doComplete.get(3).getLabel());
}

// public static final String SOURCE_DIRECTORY_ELT = "sourceDirectory";
@Test
public void testWebResourcesSourceDirectory() throws Exception {
DOMDocument document = createDOMDocument("/local-path/pom-webresource.xml", languageService);
List<CompletionItem> doComplete = languageService.doComplete(document, new Position(1, 17), new SharedSettings()).getItems();

// parent folder, then direct children
assertEquals("..", doComplete.get(0).getLabel());
assertEquals("child-parent", doComplete.get(1).getLabel());
assertEquals("folder1", doComplete.get(2).getLabel());
assertEquals("folder2", doComplete.get(3).getLabel());
}

// public static final String SCRIPT_SOURCE_DIRECTORY_ELT = "scriptSourceDirectory";
@Test
public void testWebResourcesScriptSourceDirectory() throws Exception {
DOMDocument document = createDOMDocument("/local-path/pom-webresource.xml", languageService);
List<CompletionItem> doComplete = languageService.doComplete(document, new Position(2, 23), new SharedSettings()).getItems();

// parent folder, then direct children
assertEquals("..", doComplete.get(0).getLabel());
assertEquals("child-parent", doComplete.get(1).getLabel());
assertEquals("folder1", doComplete.get(2).getLabel());
assertEquals("folder2", doComplete.get(3).getLabel());
}

// public static final String TEST_SOURCE_DIRECTORY_ELT = "testSourceDirectory";
@Test
public void testWebResourcesTestSourceDirectory() throws Exception {
DOMDocument document = createDOMDocument("/local-path/pom-webresource.xml", languageService);
List<CompletionItem> doComplete = languageService.doComplete(document, new Position(3, 21), new SharedSettings()).getItems();

// parent folder, then direct children
assertEquals("..", doComplete.get(0).getLabel());
assertEquals("child-parent", doComplete.get(1).getLabel());
assertEquals("folder1", doComplete.get(2).getLabel());
assertEquals("folder2", doComplete.get(3).getLabel());
}

// public static final String OUTPUT_DIRECTORY_ELT = "outputDirectory";
@Test
public void testWebResourcesOutputDirectory() throws Exception {
DOMDocument document = createDOMDocument("/local-path/pom-webresource.xml", languageService);
List<CompletionItem> doComplete = languageService.doComplete(document, new Position(4, 17), new SharedSettings()).getItems();

// parent folder, then direct children
assertEquals("..", doComplete.get(0).getLabel());
assertEquals("child-parent", doComplete.get(1).getLabel());
assertEquals("folder1", doComplete.get(2).getLabel());
assertEquals("folder2", doComplete.get(3).getLabel());
}

// public static final String TEST_OUTPUT_DIRECTORY_ELT = "testOutputDirectory";
@Test
public void testWebResourcesTestOutputDirectory() throws Exception {
DOMDocument document = createDOMDocument("/local-path/pom-webresource.xml", languageService);
List<CompletionItem> doComplete = languageService.doComplete(document, new Position(5, 21), new SharedSettings()).getItems();

// parent folder, then direct children
assertEquals("..", doComplete.get(0).getLabel());
assertEquals("child-parent", doComplete.get(1).getLabel());
assertEquals("folder1", doComplete.get(2).getLabel());
assertEquals("folder2", doComplete.get(3).getLabel());
}

// public static final String FILTERS_ELT = "filters";
// public static final String FILTER_ELT = "filter";
@Test
public void testWebResourcesFilter() throws Exception {
DOMDocument document = createDOMDocument("/local-path/pom-webresource.xml", languageService);
List<CompletionItem> doComplete = languageService.doComplete(document, new Position(6, 17), new SharedSettings()).getItems();

// parent folder, then direct children
assertEquals("filter.properties", doComplete.get(0).getLabel());
assertEquals("child-parent/filter.properties", doComplete.get(1).getLabel());
assertEquals("folder1/filter.properties", doComplete.get(2).getLabel());
assertEquals("folder2/filter.properties", doComplete.get(3).getLabel());
}

// public static final String FILE_ELT = "file";
// public static final String EXISTS_ELT = "exists";
@Test
public void testWebResourcesFileExists() throws Exception {
DOMDocument document = createDOMDocument("/local-path/pom-webresource.xml", languageService);
List<CompletionItem> doComplete = languageService.doComplete(document, new Position(7, 14), new SharedSettings()).getItems();

// files
assertEquals("filter.properties", doComplete.get(0).getLabel());
assertEquals("pom-webresource.xml", doComplete.get(1).getLabel());
assertEquals("pom.xml", doComplete.get(2).getLabel());

// parent folder, then direct children
assertEquals("..", doComplete.get(3).getLabel());
assertEquals("child-parent", doComplete.get(4).getLabel());
assertEquals("folder1", doComplete.get(5).getLabel());
assertEquals("folder2", doComplete.get(6).getLabel());
}

// public static final String FILE_ELT = "file";
// public static final String MISSING_ELT = "missing";
@Test
public void testWebResourcesFileMissing() throws Exception {
DOMDocument document = createDOMDocument("/local-path/pom-webresource.xml", languageService);
List<CompletionItem> doComplete = languageService.doComplete(document, new Position(8, 15), new SharedSettings()).getItems();

// files
assertEquals("filter.properties", doComplete.get(0).getLabel());
assertEquals("pom-webresource.xml", doComplete.get(1).getLabel());
assertEquals("pom.xml", doComplete.get(2).getLabel());

// parent folder, then direct children
assertEquals("..", doComplete.get(3).getLabel());
assertEquals("child-parent", doComplete.get(4).getLabel());
assertEquals("folder1", doComplete.get(5).getLabel());
assertEquals("folder2", doComplete.get(6).getLabel());
}

}
Empty file.
Empty file.
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<directory></directory>
<sourceDirectory></sourceDirectory>
<scriptSourceDirectory></scriptSourceDirectory>
<testSourceDirectory></testSourceDirectory>
<outputDirectory></outputDirectory>
<testOutputDirectory></testOutputDirectory>
<filters><filter></filter></filters>
<file><exists></exists></file>
<file><missing></missing></file>

0 comments on commit c14bb4f

Please sign in to comment.