Skip to content

Commit

Permalink
#939: fix NPE on path completion and implement repository completion (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
hohwille authored Jan 20, 2025
1 parent 99b07ff commit 6735f7b
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.List;

import com.devonfw.tools.ide.context.IdeContext;
import com.devonfw.tools.ide.git.GitContext;
import com.devonfw.tools.ide.property.RepositoryProperty;
import com.devonfw.tools.ide.tool.ToolCommandlet;

Expand Down Expand Up @@ -45,21 +46,13 @@ public void run() {
doImportRepository(repositoryFile, true);
} else {
// If no specific repository is provided, check for repositories folder
Path repositoriesPath = this.context.getSettingsPath().resolve(IdeContext.FOLDER_REPOSITORIES);
Path legacyRepositoriesPath = this.context.getSettingsPath().resolve(IdeContext.FOLDER_LEGACY_REPOSITORIES);
Path repositories;
if (Files.exists(repositoriesPath)) {
repositories = repositoriesPath;
} else if (Files.exists(legacyRepositoriesPath)) {
repositories = legacyRepositoriesPath;
} else {
this.context.warning("Cannot find repositories folder nor projects folder.");
Path repositoriesPath = this.context.getRepositoriesPath();
if (repositoriesPath == null) {
this.context.warning("Cannot find folder 'repositories' nor 'projects' in your settings.");
return;
}

List<Path> propertiesFiles = this.context.getFileAccess()
.listChildren(repositories, path -> path.getFileName().toString().endsWith(".properties"));

.listChildren(repositoriesPath, path -> path.getFileName().toString().endsWith(".properties"));
boolean forceMode = this.context.isForceMode();
for (Path propertiesFile : propertiesFiles) {
doImportRepository(propertiesFile, forceMode);
Expand Down Expand Up @@ -91,12 +84,17 @@ private void doImportRepository(Path repositoryFile, boolean forceMode) {

this.context.debug(repositoryConfig.toString());

String workspace = repositoryConfig.workspace() != null ? repositoryConfig.workspace() : "main";
Path workspacePath = this.context.getIdeHome().resolve("workspaces").resolve(workspace);
String workspace = repositoryConfig.workspace();
if (workspace == null) {
workspace = IdeContext.WORKSPACE_MAIN;
}
Path workspacePath = this.context.getIdeHome().resolve(IdeContext.FOLDER_WORKSPACES).resolve(workspace);
this.context.getFileAccess().mkdirs(workspacePath);

Path repositoryPath = workspacePath.resolve(repository);
this.context.getGitContext().pullOrClone(repositoryConfig.asGitUrl(), repositoryPath);
if (!Files.isDirectory(repositoryPath.resolve(GitContext.GIT_FOLDER))) {
this.context.getGitContext().pullOrClone(repositoryConfig.asGitUrl(), repositoryPath);
}

String buildCmd = repositoryConfig.buildCmd();
this.context.debug("Building repository with ide command: {}", buildCmd);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import com.devonfw.tools.ide.cli.CliArgument;
import com.devonfw.tools.ide.cli.CliArguments;
import com.devonfw.tools.ide.cli.CliException;
import com.devonfw.tools.ide.completion.IdeCompleter;
import com.devonfw.tools.ide.context.AbstractIdeContext;
import com.devonfw.tools.ide.context.IdeContext;
Expand Down Expand Up @@ -112,6 +113,8 @@ public void run() {
} catch (IOException e) {
throw new RuntimeException(e);
}
} catch (CliException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException("Unexpected error during interactive auto-completion", e);
}
Expand Down
29 changes: 25 additions & 4 deletions cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ public interface IdeContext extends IdeStartContext {
String FILE_CUSTOM_TOOLS = "ide-custom-tools.json";

/**
* file containing the current local commit hash of the settings repository. */
* file containing the current local commit hash of the settings repository.
*/
String SETTINGS_COMMIT_ID = ".commit.id";

/**
Expand Down Expand Up @@ -354,13 +355,33 @@ default void requireOnline(String purpose) {
Path getUserHomeIde();

/**
* @return the {@link Path} to the {@code settings} folder with the cloned git repository containing the project configuration.
* @return the {@link Path} to the {@link #FOLDER_SETTINGS settings} folder with the cloned git repository containing the project configuration.
*/
Path getSettingsPath();

/**
*
* @return the {@link Path} to the {@code settings} folder with the cloned git repository containing the project configuration only if the settings repository is in fact a git repository.
* @return the {@link Path} to the {@link #FOLDER_REPOSITORIES repositories} folder with legacy fallback if not present or {@code null} if not found.
*/
default Path getRepositoriesPath() {

Path settingsPath = getSettingsPath();
if (settingsPath == null) {
return null;
}
Path repositoriesPath = settingsPath.resolve(IdeContext.FOLDER_REPOSITORIES);
if (Files.isDirectory(repositoriesPath)) {
return repositoriesPath;
}
Path legacyRepositoriesPath = settingsPath.resolve(IdeContext.FOLDER_LEGACY_REPOSITORIES);
if (Files.isDirectory(legacyRepositoriesPath)) {
return legacyRepositoriesPath;
}
return null;
}

/**
* @return the {@link Path} to the {@code settings} folder with the cloned git repository containing the project configuration only if the settings repository
* is in fact a git repository.
*/
Path getSettingsGitRepository();

Expand Down
40 changes: 34 additions & 6 deletions cli/src/main/java/com/devonfw/tools/ide/property/PathProperty.java
Original file line number Diff line number Diff line change
Expand Up @@ -109,21 +109,49 @@ protected void completeValue(String arg, IdeContext context, Commandlet commandl

Path path = Path.of(arg);
Path parent = path.getParent();
//set a default parent directory when unable to obtain the parent directory
if (parent == null) {
parent = Path.of(".");
}
String filename = path.getFileName().toString();
if (Files.isDirectory(parent)) {
try (Stream<Path> children = Files.list(parent)) {
children.filter(child -> isValidPath(path, filename)).forEach(child -> collector.add(child.toString(), null, this, commandlet));
completeValuesFromFolder(parent, filename, context, commandlet, collector);
}

/**
* @param folder the {@link Path} to the directory where to search for the file.
* @param filename the filename (prefix) to complete.
* @param context the {@link IdeContext}.
* @param commandlet the owning {@link Commandlet}.
* @param collector the {@link CompletionCandidateCollector}.
*/
protected void completeValuesFromFolder(Path folder, String filename, IdeContext context, Commandlet commandlet, CompletionCandidateCollector collector) {

if (Files.isDirectory(folder)) {
try (Stream<Path> children = Files.list(folder)) {
children.filter(child -> isValidPath(child, filename))
.forEach(child -> collector.add(getPathForCompletion(child, context, commandlet), null, this, commandlet));
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
}

/**
* @param path the {@link Path} that has been found via completion.
* @param context the {@link IdeContext}.
* @param commandlet the owning {@link Commandlet}.
* @return the {@link String} to {@link CompletionCandidateCollector#add(String, String, Property, Commandlet) add} as completion candidate.
*/
protected String getPathForCompletion(Path path, IdeContext context, Commandlet commandlet) {

return path.toString();
}

private boolean isValidPath(Path path, String filename) {

if (isPathRequiredToBeFile() && !Files.isRegularFile(getValue())) {
return false;
} else if (isPathRequiredToBeFolder() && !Files.isDirectory(getValue())) {
if (isPathRequiredToBeFile() && !Files.isRegularFile(path)) {
return false; // isnt this wrong? How can I use completion to complete a file in a sub-folder?
} else if (isPathRequiredToBeFolder() && !Files.isDirectory(path)) {
return false;
}
return path.getFileName().toString().startsWith(filename);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;

import com.devonfw.tools.ide.commandlet.Commandlet;
import com.devonfw.tools.ide.completion.CompletionCandidateCollector;
import com.devonfw.tools.ide.context.IdeContext;
import com.devonfw.tools.ide.validation.PropertyValidator;

Expand All @@ -12,6 +13,8 @@
*/
public class RepositoryProperty extends FileProperty {

public static final String EXTENSION_PROPERTIES = ".properties";

/**
* The constructor.
*
Expand Down Expand Up @@ -43,22 +46,45 @@ public Path parse(String valueAsString, IdeContext context) {
if (valueAsString == null) {
return null;
}

Path repositoriesPath = null;
Path repositoryFile = Path.of(valueAsString);
if (!Files.exists(repositoryFile)) {
Path repositoriesPath = context.getSettingsPath().resolve(IdeContext.FOLDER_REPOSITORIES);
Path legacyRepositoriesPath = context.getSettingsPath().resolve(IdeContext.FOLDER_LEGACY_REPOSITORIES);
String propertiesFileName = valueAsString;
if (!valueAsString.endsWith(".properties")) {
propertiesFileName += ".properties";
repositoryFile = null;
repositoriesPath = context.getRepositoriesPath();
if (repositoriesPath != null) {
String propertiesFileName = valueAsString;
if (!valueAsString.endsWith(EXTENSION_PROPERTIES)) {
propertiesFileName += EXTENSION_PROPERTIES;
}
Path resolvedRepositoriesFile = repositoriesPath.resolve(propertiesFileName);
if (Files.exists(resolvedRepositoriesFile)) {
repositoryFile = resolvedRepositoriesFile;
}
}
repositoryFile = context.getFileAccess().findExistingFile(propertiesFileName,
Arrays.asList(repositoriesPath, legacyRepositoriesPath));
}
if (repositoryFile == null) {
throw new IllegalStateException("Could not find properties file: " + valueAsString);
throw new IllegalStateException("Could not find properties file: " + valueAsString + " in " + repositoriesPath);
}
return repositoryFile;
}

@Override
protected void completeValue(String arg, IdeContext context, Commandlet commandlet, CompletionCandidateCollector collector) {

Path repositoriesPath = context.getRepositoriesPath();
if (repositoriesPath != null) {
completeValuesFromFolder(repositoriesPath, arg, context, commandlet, collector);
}
}

@Override
protected String getPathForCompletion(Path path, IdeContext context, Commandlet commandlet) {

String filename = path.getFileName().toString();
if (filename.endsWith(EXTENSION_PROPERTIES)) {
filename = filename.substring(0, filename.length() - EXTENSION_PROPERTIES.length());
}
return filename;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public void testRunNoRepositoriesOrProjectsFolderFound() {
// act
rc.run();
// assert
assertThat(this.context).logAtWarning().hasMessage("Cannot find repositories folder nor projects folder.");
assertThat(this.context).logAtWarning().hasMessage("Cannot find folder 'repositories' nor 'projects' in your settings.");
}

private void createPropertiesFile() {
Expand Down

0 comments on commit 6735f7b

Please sign in to comment.