Skip to content

Commit 6735f7b

Browse files
authored
#939: fix NPE on path completion and implement repository completion (#956)
1 parent 99b07ff commit 6735f7b

File tree

6 files changed

+112
-36
lines changed

6 files changed

+112
-36
lines changed

cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.util.List;
66

77
import com.devonfw.tools.ide.context.IdeContext;
8+
import com.devonfw.tools.ide.git.GitContext;
89
import com.devonfw.tools.ide.property.RepositoryProperty;
910
import com.devonfw.tools.ide.tool.ToolCommandlet;
1011

@@ -45,21 +46,13 @@ public void run() {
4546
doImportRepository(repositoryFile, true);
4647
} else {
4748
// If no specific repository is provided, check for repositories folder
48-
Path repositoriesPath = this.context.getSettingsPath().resolve(IdeContext.FOLDER_REPOSITORIES);
49-
Path legacyRepositoriesPath = this.context.getSettingsPath().resolve(IdeContext.FOLDER_LEGACY_REPOSITORIES);
50-
Path repositories;
51-
if (Files.exists(repositoriesPath)) {
52-
repositories = repositoriesPath;
53-
} else if (Files.exists(legacyRepositoriesPath)) {
54-
repositories = legacyRepositoriesPath;
55-
} else {
56-
this.context.warning("Cannot find repositories folder nor projects folder.");
49+
Path repositoriesPath = this.context.getRepositoriesPath();
50+
if (repositoriesPath == null) {
51+
this.context.warning("Cannot find folder 'repositories' nor 'projects' in your settings.");
5752
return;
5853
}
59-
6054
List<Path> propertiesFiles = this.context.getFileAccess()
61-
.listChildren(repositories, path -> path.getFileName().toString().endsWith(".properties"));
62-
55+
.listChildren(repositoriesPath, path -> path.getFileName().toString().endsWith(".properties"));
6356
boolean forceMode = this.context.isForceMode();
6457
for (Path propertiesFile : propertiesFiles) {
6558
doImportRepository(propertiesFile, forceMode);
@@ -91,12 +84,17 @@ private void doImportRepository(Path repositoryFile, boolean forceMode) {
9184

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

94-
String workspace = repositoryConfig.workspace() != null ? repositoryConfig.workspace() : "main";
95-
Path workspacePath = this.context.getIdeHome().resolve("workspaces").resolve(workspace);
87+
String workspace = repositoryConfig.workspace();
88+
if (workspace == null) {
89+
workspace = IdeContext.WORKSPACE_MAIN;
90+
}
91+
Path workspacePath = this.context.getIdeHome().resolve(IdeContext.FOLDER_WORKSPACES).resolve(workspace);
9692
this.context.getFileAccess().mkdirs(workspacePath);
9793

9894
Path repositoryPath = workspacePath.resolve(repository);
99-
this.context.getGitContext().pullOrClone(repositoryConfig.asGitUrl(), repositoryPath);
95+
if (!Files.isDirectory(repositoryPath.resolve(GitContext.GIT_FOLDER))) {
96+
this.context.getGitContext().pullOrClone(repositoryConfig.asGitUrl(), repositoryPath);
97+
}
10098

10199
String buildCmd = repositoryConfig.buildCmd();
102100
this.context.debug("Building repository with ide command: {}", buildCmd);

cli/src/main/java/com/devonfw/tools/ide/commandlet/ShellCommandlet.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import com.devonfw.tools.ide.cli.CliArgument;
2424
import com.devonfw.tools.ide.cli.CliArguments;
25+
import com.devonfw.tools.ide.cli.CliException;
2526
import com.devonfw.tools.ide.completion.IdeCompleter;
2627
import com.devonfw.tools.ide.context.AbstractIdeContext;
2728
import com.devonfw.tools.ide.context.IdeContext;
@@ -112,6 +113,8 @@ public void run() {
112113
} catch (IOException e) {
113114
throw new RuntimeException(e);
114115
}
116+
} catch (CliException e) {
117+
throw e;
115118
} catch (Exception e) {
116119
throw new RuntimeException("Unexpected error during interactive auto-completion", e);
117120
}

cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@ public interface IdeContext extends IdeStartContext {
140140
String FILE_CUSTOM_TOOLS = "ide-custom-tools.json";
141141

142142
/**
143-
* file containing the current local commit hash of the settings repository. */
143+
* file containing the current local commit hash of the settings repository.
144+
*/
144145
String SETTINGS_COMMIT_ID = ".commit.id";
145146

146147
/**
@@ -354,13 +355,33 @@ default void requireOnline(String purpose) {
354355
Path getUserHomeIde();
355356

356357
/**
357-
* @return the {@link Path} to the {@code settings} folder with the cloned git repository containing the project configuration.
358+
* @return the {@link Path} to the {@link #FOLDER_SETTINGS settings} folder with the cloned git repository containing the project configuration.
358359
*/
359360
Path getSettingsPath();
360361

361362
/**
362-
*
363-
* @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.
363+
* @return the {@link Path} to the {@link #FOLDER_REPOSITORIES repositories} folder with legacy fallback if not present or {@code null} if not found.
364+
*/
365+
default Path getRepositoriesPath() {
366+
367+
Path settingsPath = getSettingsPath();
368+
if (settingsPath == null) {
369+
return null;
370+
}
371+
Path repositoriesPath = settingsPath.resolve(IdeContext.FOLDER_REPOSITORIES);
372+
if (Files.isDirectory(repositoriesPath)) {
373+
return repositoriesPath;
374+
}
375+
Path legacyRepositoriesPath = settingsPath.resolve(IdeContext.FOLDER_LEGACY_REPOSITORIES);
376+
if (Files.isDirectory(legacyRepositoriesPath)) {
377+
return legacyRepositoriesPath;
378+
}
379+
return null;
380+
}
381+
382+
/**
383+
* @return the {@link Path} to the {@code settings} folder with the cloned git repository containing the project configuration only if the settings repository
384+
* is in fact a git repository.
364385
*/
365386
Path getSettingsGitRepository();
366387

cli/src/main/java/com/devonfw/tools/ide/property/PathProperty.java

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,21 +109,49 @@ protected void completeValue(String arg, IdeContext context, Commandlet commandl
109109

110110
Path path = Path.of(arg);
111111
Path parent = path.getParent();
112+
//set a default parent directory when unable to obtain the parent directory
113+
if (parent == null) {
114+
parent = Path.of(".");
115+
}
112116
String filename = path.getFileName().toString();
113-
if (Files.isDirectory(parent)) {
114-
try (Stream<Path> children = Files.list(parent)) {
115-
children.filter(child -> isValidPath(path, filename)).forEach(child -> collector.add(child.toString(), null, this, commandlet));
117+
completeValuesFromFolder(parent, filename, context, commandlet, collector);
118+
}
119+
120+
/**
121+
* @param folder the {@link Path} to the directory where to search for the file.
122+
* @param filename the filename (prefix) to complete.
123+
* @param context the {@link IdeContext}.
124+
* @param commandlet the owning {@link Commandlet}.
125+
* @param collector the {@link CompletionCandidateCollector}.
126+
*/
127+
protected void completeValuesFromFolder(Path folder, String filename, IdeContext context, Commandlet commandlet, CompletionCandidateCollector collector) {
128+
129+
if (Files.isDirectory(folder)) {
130+
try (Stream<Path> children = Files.list(folder)) {
131+
children.filter(child -> isValidPath(child, filename))
132+
.forEach(child -> collector.add(getPathForCompletion(child, context, commandlet), null, this, commandlet));
116133
} catch (IOException e) {
117134
throw new IllegalStateException(e);
118135
}
119136
}
120137
}
121138

139+
/**
140+
* @param path the {@link Path} that has been found via completion.
141+
* @param context the {@link IdeContext}.
142+
* @param commandlet the owning {@link Commandlet}.
143+
* @return the {@link String} to {@link CompletionCandidateCollector#add(String, String, Property, Commandlet) add} as completion candidate.
144+
*/
145+
protected String getPathForCompletion(Path path, IdeContext context, Commandlet commandlet) {
146+
147+
return path.toString();
148+
}
149+
122150
private boolean isValidPath(Path path, String filename) {
123151

124-
if (isPathRequiredToBeFile() && !Files.isRegularFile(getValue())) {
125-
return false;
126-
} else if (isPathRequiredToBeFolder() && !Files.isDirectory(getValue())) {
152+
if (isPathRequiredToBeFile() && !Files.isRegularFile(path)) {
153+
return false; // isnt this wrong? How can I use completion to complete a file in a sub-folder?
154+
} else if (isPathRequiredToBeFolder() && !Files.isDirectory(path)) {
127155
return false;
128156
}
129157
return path.getFileName().toString().startsWith(filename);

cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
import java.nio.file.Files;
44
import java.nio.file.Path;
5-
import java.util.Arrays;
65

6+
import com.devonfw.tools.ide.commandlet.Commandlet;
7+
import com.devonfw.tools.ide.completion.CompletionCandidateCollector;
78
import com.devonfw.tools.ide.context.IdeContext;
89
import com.devonfw.tools.ide.validation.PropertyValidator;
910

@@ -12,6 +13,8 @@
1213
*/
1314
public class RepositoryProperty extends FileProperty {
1415

16+
public static final String EXTENSION_PROPERTIES = ".properties";
17+
1518
/**
1619
* The constructor.
1720
*
@@ -43,22 +46,45 @@ public Path parse(String valueAsString, IdeContext context) {
4346
if (valueAsString == null) {
4447
return null;
4548
}
46-
49+
Path repositoriesPath = null;
4750
Path repositoryFile = Path.of(valueAsString);
4851
if (!Files.exists(repositoryFile)) {
49-
Path repositoriesPath = context.getSettingsPath().resolve(IdeContext.FOLDER_REPOSITORIES);
50-
Path legacyRepositoriesPath = context.getSettingsPath().resolve(IdeContext.FOLDER_LEGACY_REPOSITORIES);
51-
String propertiesFileName = valueAsString;
52-
if (!valueAsString.endsWith(".properties")) {
53-
propertiesFileName += ".properties";
52+
repositoryFile = null;
53+
repositoriesPath = context.getRepositoriesPath();
54+
if (repositoriesPath != null) {
55+
String propertiesFileName = valueAsString;
56+
if (!valueAsString.endsWith(EXTENSION_PROPERTIES)) {
57+
propertiesFileName += EXTENSION_PROPERTIES;
58+
}
59+
Path resolvedRepositoriesFile = repositoriesPath.resolve(propertiesFileName);
60+
if (Files.exists(resolvedRepositoriesFile)) {
61+
repositoryFile = resolvedRepositoriesFile;
62+
}
5463
}
55-
repositoryFile = context.getFileAccess().findExistingFile(propertiesFileName,
56-
Arrays.asList(repositoriesPath, legacyRepositoriesPath));
5764
}
5865
if (repositoryFile == null) {
59-
throw new IllegalStateException("Could not find properties file: " + valueAsString);
66+
throw new IllegalStateException("Could not find properties file: " + valueAsString + " in " + repositoriesPath);
6067
}
6168
return repositoryFile;
6269
}
6370

71+
@Override
72+
protected void completeValue(String arg, IdeContext context, Commandlet commandlet, CompletionCandidateCollector collector) {
73+
74+
Path repositoriesPath = context.getRepositoriesPath();
75+
if (repositoriesPath != null) {
76+
completeValuesFromFolder(repositoriesPath, arg, context, commandlet, collector);
77+
}
78+
}
79+
80+
@Override
81+
protected String getPathForCompletion(Path path, IdeContext context, Commandlet commandlet) {
82+
83+
String filename = path.getFileName().toString();
84+
if (filename.endsWith(EXTENSION_PROPERTIES)) {
85+
filename = filename.substring(0, filename.length() - EXTENSION_PROPERTIES.length());
86+
}
87+
return filename;
88+
}
89+
6490
}

cli/src/test/java/com/devonfw/tools/ide/commandlet/RepositoryCommandletTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public void testRunNoRepositoriesOrProjectsFolderFound() {
9393
// act
9494
rc.run();
9595
// assert
96-
assertThat(this.context).logAtWarning().hasMessage("Cannot find repositories folder nor projects folder.");
96+
assertThat(this.context).logAtWarning().hasMessage("Cannot find folder 'repositories' nor 'projects' in your settings.");
9797
}
9898

9999
private void createPropertiesFile() {

0 commit comments

Comments
 (0)