Skip to content

Commit

Permalink
FUSETOOLS2-2478 - completion for dependencies of Camel component in pom
Browse files Browse the repository at this point in the history
there will be completion provided sometimes not at the exact right-place
but should be fine for now.

to have a good ones in this iteration the user will have to use the
correct runtime provider otherwise it will end up with not specific to
their runtime dependency. To improve in the future by helping to set the
runtime provider or to look to the content of the pom.xml

Signed-off-by: Aurélien Pupier <[email protected]>
  • Loading branch information
apupier committed Sep 12, 2024
1 parent 23d7816 commit c242880
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ public CompletableFuture<Either<List<CompletionItem>, CompletionList>> completio
} else if (new CamelKModelineParser().isOnCamelKModeline(completionParams.getPosition().getLine(), textDocumentItem)){
return new CamelKModelineCompletionprocessor(textDocumentItem, getCamelCatalog()).getCompletions(completionParams.getPosition()).thenApply(Either::forLeft);
} else if(uri.endsWith("pom.xml")) {
return new PomCompletionProcessor().getCompletions().thenApply(Either::forLeft);
return new PomCompletionProcessor(textDocumentItem, getCamelCatalog()).getCompletions(completionParams.getPosition()).thenApply(Either::forLeft);
} else if(uri.endsWith("tasks.json")) {
return new VSCodeTasksCompletionProcessor(textDocumentItem).getCompletions(completionParams.getPosition()).thenApply(Either::forLeft);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +16,125 @@
*/
package com.github.cameltooling.lsp.internal.completion;

import java.util.Collections;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

import org.apache.camel.catalog.CamelCatalog;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.TextDocumentItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.cameltooling.lsp.internal.catalog.model.ComponentModel;
import com.github.cameltooling.lsp.internal.catalog.util.ModelHelper;

public class PomCompletionProcessor {

private static final Logger LOGGER = LoggerFactory.getLogger(PomCompletionProcessor.class);

private TextDocumentItem textDocumentItem;
private CompletableFuture<CamelCatalog> camelCatalog;

public PomCompletionProcessor(TextDocumentItem textDocumentItem, CompletableFuture<CamelCatalog> camelCatalog) {
this.textDocumentItem = textDocumentItem;
this.camelCatalog = camelCatalog;
}

public CompletableFuture<List<CompletionItem>> getCompletions(Position position) {
List<CompletionItem> completions = new ArrayList<>();
String text = textDocumentItem.getText();
String[] lines = text.split("\\R");
if (isInsideTag(position, lines, "profiles")) {
completions.add(createCamelQuarkusDebugProfileCompletionItem());
}

if (isInsideTag(position, lines, "dependencies")) {
try {
completions.addAll(createCamelDependenciesCompletionItems());
} catch (Exception e) {
LOGGER.warn("Error while computing Camel dependencies for Maven pom", e);
}
}

return CompletableFuture.completedFuture(completions);
}

private List<CompletionItem> createCamelDependenciesCompletionItems() throws InterruptedException, ExecutionException {
List<CompletionItem> completions = new ArrayList<>();
List<String> componentNames = camelCatalog.get().findComponentNames();
for (String componentName : componentNames) {
ComponentModel componentModel = ModelHelper.generateComponentModel(camelCatalog.get().componentJSonSchema(componentName), false);
CompletionItem completionItem = new CompletionItem("Camel dependency for component " + componentModel.getTitle());
completionItem.setInsertText(
"<dependency>\n" +
"\t<groupId>"+componentModel.getGroupId()+"</groupId>\n" +
"\t<artifactId>"+componentModel.getArtifactId()+"</artifactId>\n" +
"</dependency>\n");
CompletionResolverUtils.applyDeprecation(completionItem, componentModel.getDeprecated());
completions.add(completionItem);
}
return completions;
}

public CompletableFuture<List<CompletionItem>> getCompletions() {
private CompletionItem createCamelQuarkusDebugProfileCompletionItem() {
CompletionItem completionQuarkusDebugProfile = new CompletionItem("Camel debug profile for Quarkus");
completionQuarkusDebugProfile.setInsertText(
"<profile>\n"
+ " <id>camel.debug</id>\n"
+ " <activation>\n"
+ " <property>\n"
+ " <name>camel.debug</name>\n"
+ " <value>true</value>\n"
+ " </property>\n"
+ " </activation>\n"
+ " <dependencies>\n"
+ " <dependency>\n"
+ " <groupId>org.apache.camel.quarkus</groupId>\n"
+ " <artifactId>camel-quarkus-debug</artifactId>\n"
+ " </dependency>\n"
+ " </dependencies>\n"
+ "</profile>");
"""
<profile>
\t<id>camel.debug</id>
\t<activation>
\t\t<property>
\t\t\t<name>camel.debug</name>
\t\t\t<value>true</value>
\t\t</property>
\t</activation>
\t<dependencies>
\t\t<dependency>
\t\t\t<groupId>org.apache.camel.quarkus</groupId>
\t\t\t<artifactId>camel-quarkus-debug</artifactId>
\t\t</dependency>
\t</dependencies>
</profile>""");
completionQuarkusDebugProfile.setDocumentation(
"Adding a profile to enable Camel Debug.\n"
+ "Combined with launch configuration and tasks, it allows single-click debug.\n"
+ "It requires Camel Quarkus 2.14+");
return CompletableFuture.completedFuture(Collections.singletonList(completionQuarkusDebugProfile));
"""
Adding a profile to enable Camel Debug.
Combined with launch configuration and tasks, it allows single-click debug.
It requires Camel Quarkus 2.14+""");
return completionQuarkusDebugProfile;
}

/**
* /!\ This can lead to some false positive but I think this is not really important for now.
*
* @param position
* @param lines
* @param enclosingTag
* @return
*/
private boolean isInsideTag(Position position, String[] lines, String enclosingTag) {
boolean hasEnclosingTagBefore = false;
boolean hasEnclosingTagAfter = false;
for (int i = position.getLine(); i >= 0 && i < lines.length; i--) {
if (lines[i].contains("<"+enclosingTag+">")) {
hasEnclosingTagBefore = true;
break;
}
if (lines[i].contains("</"+enclosingTag+">")) {
return false;
}
}

for (int i = position.getLine(); i < lines.length; i++) {
if (lines[i].contains("</"+enclosingTag+">")) {
hasEnclosingTagAfter = true;
break;
}
}

return hasEnclosingTagBefore && hasEnclosingTagAfter;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,85 @@

class PomCompletionTest extends AbstractCamelLanguageServerTest {

@Test
void testNoCompletionOutsideTag() throws Exception {
CamelLanguageServer languageServer = initializeLanguageServerWithFileName("""
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
<profiles>
</profiles>
</project>\
""", "pom.xml");
List<CompletionItem> completionItems = getCompletionFor(languageServer, new Position(4, 5), "pom.xml").get().getLeft();
assertThat(completionItems).isEmpty();
}

@Test
void testNoCompletionOutsideTagEvenWithDependencyManagement() throws Exception {
CamelLanguageServer languageServer = initializeLanguageServerWithFileName("""
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
<dependencyManagement>
<dependencies>
</dependencies>
</dependencyManagement>
<build>
<dependencies>
</dependencies>
</build>
</project>\
""", "pom.xml");
List<CompletionItem> completionItems = getCompletionFor(languageServer, new Position(9, 5), "pom.xml").get().getLeft();
assertThat(completionItems).isEmpty();
}

@Test
void testQuarkusDebugProfile() throws Exception {
CamelLanguageServer languageServer = initializeLanguageServerWithFileName("", "pom.xml");
List<CompletionItem> completionItems = getCompletionFor(languageServer, new Position(0, 0), "pom.xml").get().getLeft();
CamelLanguageServer languageServer = initializeLanguageServerWithFileName("""
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
<profiles>
</profiles>
</project>\
""", "pom.xml");
List<CompletionItem> completionItems = getCompletionFor(languageServer, new Position(6, 5), "pom.xml").get().getLeft();
assertThat(completionItems).hasSize(1);
assertThat(completionItems.get(0).getLabel()).isEqualTo("Camel debug profile for Quarkus");
assertThat(completionItems.get(0).getInsertText()).isNotNull();
}

@Test
void testCamelComponentDependency() throws Exception {
CamelLanguageServer languageServer = initializeLanguageServerWithFileName("""
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
<build>
<dependencies>
</dependencies>
</build>
</project>\
""", "pom.xml");
List<CompletionItem> completionItems = getCompletionFor(languageServer, new Position(7, 5), "pom.xml").get().getLeft();
assertThat(completionItems).isNotEmpty();
assertThat(completionItems.get(0).getLabel()).startsWith("Camel dependency for component");
assertThat(completionItems.get(0).getInsertText()).isNotNull();
}

}

0 comments on commit c242880

Please sign in to comment.