diff --git a/build.gradle b/build.gradle index 3637f269..98ef01d8 100644 --- a/build.gradle +++ b/build.gradle @@ -28,8 +28,7 @@ repositories { } dependencies { - implementation "org.eclipse.lsp4j:org.eclipse.lsp4j:0.12.0" - implementation "org.eclipse.lsp4j:org.eclipse.lsp4j.jsonrpc:0.12.0" + implementation "org.eclipse.lsp4j:org.eclipse.lsp4j:0.20.0" implementation "org.apache.groovy:groovy:4.0.26" implementation "com.google.code.gson:gson:2.13.1" implementation "io.github.classgraph:classgraph:4.8.179" diff --git a/src/main/java/net/prominic/groovyls/GroovyServices.java b/src/main/java/net/prominic/groovyls/GroovyServices.java index b234642a..5ca2ff92 100644 --- a/src/main/java/net/prominic/groovyls/GroovyServices.java +++ b/src/main/java/net/prominic/groovyls/GroovyServices.java @@ -79,6 +79,7 @@ import org.eclipse.lsp4j.TypeDefinitionParams; import org.eclipse.lsp4j.VersionedTextDocumentIdentifier; import org.eclipse.lsp4j.WorkspaceEdit; +import org.eclipse.lsp4j.WorkspaceSymbol; import org.eclipse.lsp4j.WorkspaceSymbolParams; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.lsp4j.services.LanguageClient; @@ -359,7 +360,7 @@ public CompletableFuture>> docume } @Override - public CompletableFuture> symbol(WorkspaceSymbolParams params) { + public CompletableFuture, List>> symbol(WorkspaceSymbolParams params) { WorkspaceSymbolProvider provider = new WorkspaceSymbolProvider(astVisitor); return provider.provideWorkspaceSymbols(params.getQuery()); } diff --git a/src/main/java/net/prominic/groovyls/providers/CompletionProvider.java b/src/main/java/net/prominic/groovyls/providers/CompletionProvider.java index a8802ed0..9ebf0b4d 100644 --- a/src/main/java/net/prominic/groovyls/providers/CompletionProvider.java +++ b/src/main/java/net/prominic/groovyls/providers/CompletionProvider.java @@ -36,6 +36,7 @@ import org.codehaus.groovy.ast.ImportNode; import org.codehaus.groovy.ast.MethodNode; import org.codehaus.groovy.ast.ModuleNode; +import org.codehaus.groovy.ast.Parameter; import org.codehaus.groovy.ast.PropertyNode; import org.codehaus.groovy.ast.VariableScope; import org.codehaus.groovy.ast.expr.ConstructorCallExpression; @@ -48,6 +49,7 @@ import org.eclipse.lsp4j.CompletionContext; import org.eclipse.lsp4j.CompletionItem; import org.eclipse.lsp4j.CompletionItemKind; +import org.eclipse.lsp4j.CompletionItemLabelDetails; import org.eclipse.lsp4j.CompletionList; import org.eclipse.lsp4j.MarkupContent; import org.eclipse.lsp4j.MarkupKind; @@ -293,6 +295,9 @@ private void populateItemsFromPropertiesAndFields(List properties, if (markdownDocs != null) { item.setDocumentation(new MarkupContent(MarkupKind.MARKDOWN, markdownDocs)); } + CompletionItemLabelDetails labelDetails = new CompletionItemLabelDetails(); + labelDetails.setDescription(property.getType().getNameWithoutPackage()); + item.setLabelDetails(labelDetails); return item; }).collect(Collectors.toList()); items.addAll(propItems); @@ -312,6 +317,9 @@ private void populateItemsFromPropertiesAndFields(List properties, if (markdownDocs != null) { item.setDocumentation(new MarkupContent(MarkupKind.MARKDOWN, markdownDocs)); } + CompletionItemLabelDetails labelDetails = new CompletionItemLabelDetails(); + labelDetails.setDescription(field.getType().getNameWithoutPackage()); + item.setLabelDetails(labelDetails); return item; }).collect(Collectors.toList()); items.addAll(fieldItems); @@ -330,6 +338,17 @@ private void populateItemsFromMethods(List methods, String memberNam }).map(method -> { CompletionItem item = new CompletionItem(); item.setLabel(method.getName()); + String methodParams = "("; + for (Parameter p : method.getParameters()) { + methodParams += p.getType().getNameWithoutPackage() + ' ' + p.getName() + ", "; + } + if (!methodParams.equals("(")) + methodParams = methodParams.substring(0, methodParams.length() - 2); + methodParams += ')'; + CompletionItemLabelDetails labelDetails = new CompletionItemLabelDetails(); + labelDetails.setDetail(methodParams); + labelDetails.setDescription(method.getReturnType().getNameWithoutPackage()); + item.setLabelDetails(labelDetails); item.setKind(GroovyLanguageServerUtils.astNodeToCompletionItemKind(method)); String markdownDocs = GroovydocUtils.groovydocToMarkdownDescription(method.getGroovydoc()); if (markdownDocs != null) { @@ -366,6 +385,9 @@ private void populateItemsFromVariableScope(VariableScope variableScope, String CompletionItem item = new CompletionItem(); item.setLabel(variable.getName()); item.setKind(GroovyLanguageServerUtils.astNodeToCompletionItemKind((ASTNode) variable)); + CompletionItemLabelDetails labelDetails = new CompletionItemLabelDetails(); + labelDetails.setDescription(variable.getType().getNameWithoutPackage()); + item.setLabelDetails(labelDetails); if (variable instanceof AnnotatedNode) { AnnotatedNode annotatedVar = (AnnotatedNode) variable; String markdownDocs = GroovydocUtils.groovydocToMarkdownDescription(annotatedVar.getGroovydoc()); @@ -437,7 +459,9 @@ private void populateTypes(ASTNode offsetNode, String namePrefix, Set ex CompletionItem item = new CompletionItem(); item.setLabel(classNode.getNameWithoutPackage()); item.setKind(GroovyLanguageServerUtils.astNodeToCompletionItemKind(classNode)); - item.setDetail(packageName); + CompletionItemLabelDetails labelDetails = new CompletionItemLabelDetails(); + labelDetails.setDescription(packageName); + item.setLabelDetails(labelDetails); String markdownDocs = GroovydocUtils.groovydocToMarkdownDescription(classNode.getGroovydoc()); if (markdownDocs != null) { item.setDocumentation(new MarkupContent(MarkupKind.MARKDOWN, markdownDocs)); @@ -477,8 +501,10 @@ private void populateTypes(ASTNode offsetNode, String namePrefix, Set ex String packageName = classInfo.getPackageName(); CompletionItem item = new CompletionItem(); item.setLabel(classInfo.getSimpleName()); - item.setDetail(packageName); item.setKind(classInfoToCompletionItemKind(classInfo)); + CompletionItemLabelDetails labelDetails = new CompletionItemLabelDetails(); + labelDetails.setDescription(packageName); + item.setLabelDetails(labelDetails); if (packageName != null && !packageName.equals(enclosingPackageName) && !importNames.contains(className)) { List additionalTextEdits = new ArrayList<>(); TextEdit addImportEdit = createAddImportTextEdit(className, addImportRange); diff --git a/src/main/java/net/prominic/groovyls/providers/WorkspaceSymbolProvider.java b/src/main/java/net/prominic/groovyls/providers/WorkspaceSymbolProvider.java index 424ccdbb..baf37673 100644 --- a/src/main/java/net/prominic/groovyls/providers/WorkspaceSymbolProvider.java +++ b/src/main/java/net/prominic/groovyls/providers/WorkspaceSymbolProvider.java @@ -31,6 +31,8 @@ import org.codehaus.groovy.ast.MethodNode; import org.codehaus.groovy.ast.PropertyNode; import org.eclipse.lsp4j.SymbolInformation; +import org.eclipse.lsp4j.WorkspaceSymbol; +import org.eclipse.lsp4j.jsonrpc.messages.Either; import net.prominic.groovyls.compiler.ast.ASTNodeVisitor; import net.prominic.groovyls.compiler.util.GroovyASTUtils; @@ -43,11 +45,11 @@ public WorkspaceSymbolProvider(ASTNodeVisitor ast) { this.ast = ast; } - public CompletableFuture> provideWorkspaceSymbols(String query) { + public CompletableFuture, List>> provideWorkspaceSymbols(String query) { if (ast == null) { // this shouldn't happen, but let's avoid an exception if something // goes terribly wrong. - return CompletableFuture.completedFuture(Collections.emptyList()); + return CompletableFuture.completedFuture(Either.forLeft(Collections.emptyList())); } String lowerCaseQuery = query.toLowerCase(); List nodes = ast.getNodes(); @@ -92,6 +94,6 @@ public CompletableFuture> provideWorkspaceSymb // this should never happen return null; }).filter(symbolInformation -> symbolInformation != null).collect(Collectors.toList()); - return CompletableFuture.completedFuture(symbols); + return CompletableFuture.completedFuture(Either.forLeft(symbols)); } } \ No newline at end of file diff --git a/src/test/java/net/prominic/groovyls/GroovyServicesCompletionTests.java b/src/test/java/net/prominic/groovyls/GroovyServicesCompletionTests.java index 17dc8faa..eafdd31d 100644 --- a/src/test/java/net/prominic/groovyls/GroovyServicesCompletionTests.java +++ b/src/test/java/net/prominic/groovyls/GroovyServicesCompletionTests.java @@ -123,7 +123,9 @@ void testMemberAccessOnLocalVariableAfterDot() throws Exception { List items = result.getLeft(); Assertions.assertTrue(items.size() > 0); List filteredItems = items.stream().filter(item -> { - return item.getLabel().equals("charAt") && item.getKind().equals(CompletionItemKind.Method); + return item.getLabel().equals("charAt") && item.getKind().equals(CompletionItemKind.Method) && + item.getLabelDetails().getDetail().equals("(int arg0)") && + item.getLabelDetails().getDescription().equals("char"); }).collect(Collectors.toList()); Assertions.assertEquals(1, filteredItems.size()); } @@ -149,7 +151,9 @@ void testMemberAccessOnMemberVariableAfterDot() throws Exception { List items = result.getLeft(); Assertions.assertTrue(items.size() > 0); List filteredItems = items.stream().filter(item -> { - return item.getLabel().equals("charAt") && item.getKind().equals(CompletionItemKind.Method); + return item.getLabel().equals("charAt") && item.getKind().equals(CompletionItemKind.Method) && + item.getLabelDetails().getDetail().equals("(int arg0)") && + item.getLabelDetails().getDescription().equals("char"); }).collect(Collectors.toList()); Assertions.assertEquals(1, filteredItems.size()); } @@ -175,7 +179,8 @@ void testMemberAccessOnThisAfterDot() throws Exception { List items = result.getLeft(); Assertions.assertTrue(items.size() > 0); List filteredItems = items.stream().filter(item -> { - return item.getLabel().equals("memberVar") && item.getKind().equals(CompletionItemKind.Field); + return item.getLabel().equals("memberVar") && item.getKind().equals(CompletionItemKind.Field) && + item.getLabelDetails().getDescription().equals("String"); }).collect(Collectors.toList()); Assertions.assertEquals(1, filteredItems.size()); } @@ -201,7 +206,8 @@ void testMemberAccessOnClassAfterDot() throws Exception { List items = result.getLeft(); Assertions.assertTrue(items.size() > 0); List filteredItems = items.stream().filter(item -> { - return item.getLabel().equals("staticMethod") && item.getKind().equals(CompletionItemKind.Method); + return item.getLabel().equals("staticMethod") && item.getKind().equals(CompletionItemKind.Method) && + item.getLabelDetails().getDescription().equals("void"); }).collect(Collectors.toList()); Assertions.assertEquals(1, filteredItems.size()); } @@ -227,7 +233,9 @@ void testMemberAccessOnLocalArrayAfterDot() throws Exception { List items = result.getLeft(); Assertions.assertTrue(items.size() > 0); List filteredItems = items.stream().filter(item -> { - return item.getLabel().equals("charAt") && item.getKind().equals(CompletionItemKind.Method); + return item.getLabel().equals("charAt") && item.getKind().equals(CompletionItemKind.Method) && + item.getLabelDetails().getDetail().equals("(int arg0)") && + item.getLabelDetails().getDescription().equals("char"); }).collect(Collectors.toList()); Assertions.assertEquals(1, filteredItems.size()); } @@ -252,7 +260,9 @@ void testMemberAccessOnLocalVariableWithPartialPropertyExpression() throws Excep Assertions.assertTrue(result.isLeft()); List items = result.getLeft(); List filteredItems = items.stream().filter(item -> { - return item.getLabel().equals("charAt") && item.getKind().equals(CompletionItemKind.Method); + return item.getLabel().equals("charAt") && item.getKind().equals(CompletionItemKind.Method) && + item.getLabelDetails().getDetail().equals("(int arg0)") && + item.getLabelDetails().getDescription().equals("char"); }).collect(Collectors.toList()); Assertions.assertEquals(1, filteredItems.size()); } @@ -282,8 +292,12 @@ void testMemberAccessOnThisWithMultipleResults() throws Exception { List items = result.getLeft(); Assertions.assertEquals(2, items.size()); List filteredItems = items.stream().filter(item -> { - return (item.getLabel().equals("abc") && item.getKind().equals(CompletionItemKind.Method)) - || (item.getLabel().equals("abcdef") && item.getKind().equals(CompletionItemKind.Method)); + return (item.getLabel().equals("abc") && item.getKind().equals(CompletionItemKind.Method) && + item.getLabelDetails().getDetail().equals("()") && + item.getLabelDetails().getDescription().equals("Object")) + || (item.getLabel().equals("abcdef") && item.getKind().equals(CompletionItemKind.Method) + && item.getLabelDetails().getDetail().equals("()") && + item.getLabelDetails().getDescription().equals("Object")); }).collect(Collectors.toList()); Assertions.assertEquals(2, filteredItems.size()); @@ -296,6 +310,8 @@ void testMemberAccessOnThisWithMultipleResults() throws Exception { CompletionItem item = items.get(0); Assertions.assertEquals("abcdef", item.getLabel()); Assertions.assertEquals(CompletionItemKind.Method, item.getKind()); + Assertions.assertEquals("()", item.getLabelDetails().getDetail()); + Assertions.assertEquals("Object", item.getLabelDetails().getDescription()); } @Test @@ -320,7 +336,9 @@ void testMemberAccessOnLocalVariableWithExistingVariableExpressionOnNextLine() t List items = result.getLeft(); Assertions.assertTrue(items.size() > 0); List filteredItems = items.stream().filter(item -> { - return item.getLabel().equals("charAt") && item.getKind().equals(CompletionItemKind.Method); + return item.getLabel().equals("charAt") && item.getKind().equals(CompletionItemKind.Method) && + item.getLabelDetails().getDetail().equals("(int arg0)") && + item.getLabelDetails().getDescription().equals("char"); }).collect(Collectors.toList()); Assertions.assertEquals(1, filteredItems.size()); } @@ -347,7 +365,9 @@ void testMemberAccessOnLocalVariableWithExistingMethodCallExpressionOnNextLine() List items = result.getLeft(); Assertions.assertTrue(items.size() > 0); List filteredItems = items.stream().filter(item -> { - return item.getLabel().equals("charAt") && item.getKind().equals(CompletionItemKind.Method); + return item.getLabel().equals("charAt") && item.getKind().equals(CompletionItemKind.Method) && + item.getLabelDetails().getDetail().equals("(int arg0)") && + item.getLabelDetails().getDescription().equals("char"); }).collect(Collectors.toList()); Assertions.assertEquals(1, filteredItems.size()); } @@ -372,7 +392,8 @@ void testCompletionForMemberVariableOnPartialVariableExpression() throws Excepti Assertions.assertTrue(result.isLeft()); List items = result.getLeft(); List filteredItems = items.stream().filter(item -> { - return item.getLabel().equals("memberVar") && item.getKind().equals(CompletionItemKind.Field); + return item.getLabel().equals("memberVar") && item.getKind().equals(CompletionItemKind.Field) && + item.getLabelDetails().getDescription().equals("String"); }).collect(Collectors.toList()); Assertions.assertEquals(1, filteredItems.size()); } @@ -397,7 +418,8 @@ void testCompletionForMemberVariableOnCompleteVariableExpression() throws Except Assertions.assertTrue(result.isLeft()); List items = result.getLeft(); List filteredItems = items.stream().filter(item -> { - return item.getLabel().equals("memberVar") && item.getKind().equals(CompletionItemKind.Field); + return item.getLabel().equals("memberVar") && item.getKind().equals(CompletionItemKind.Field) && + item.getLabelDetails().getDescription().equals("String"); }).collect(Collectors.toList()); Assertions.assertEquals(1, filteredItems.size()); } @@ -422,7 +444,9 @@ void testCompletionForMemberMethodOnPartialVariableExpression() throws Exception Assertions.assertTrue(result.isLeft()); List items = result.getLeft(); List filteredItems = items.stream().filter(item -> { - return item.getLabel().equals("memberMethod") && item.getKind().equals(CompletionItemKind.Method); + return item.getLabel().equals("memberMethod") && item.getKind().equals(CompletionItemKind.Method) && + item.getLabelDetails().getDetail().equals("()") && + item.getLabelDetails().getDescription().equals("String"); }).collect(Collectors.toList()); Assertions.assertEquals(1, filteredItems.size()); } @@ -447,7 +471,9 @@ void testCompletionForMemberMethodOnCompleteVariableExpression() throws Exceptio Assertions.assertTrue(result.isLeft()); List items = result.getLeft(); List filteredItems = items.stream().filter(item -> { - return item.getLabel().equals("memberMethod") && item.getKind().equals(CompletionItemKind.Method); + return item.getLabel().equals("memberMethod") && item.getKind().equals(CompletionItemKind.Method) && + item.getLabelDetails().getDetail().equals("()") && + item.getLabelDetails().getDescription().equals("String"); }).collect(Collectors.toList()); Assertions.assertEquals(1, filteredItems.size()); } @@ -471,7 +497,8 @@ void testCompletionForParameterOnPartialVariableExpression() throws Exception { Assertions.assertTrue(result.isLeft()); List items = result.getLeft(); List filteredItems = items.stream().filter(item -> { - return item.getLabel().equals("paramName") && item.getKind().equals(CompletionItemKind.Variable); + return item.getLabel().equals("paramName") && item.getKind().equals(CompletionItemKind.Variable) && + item.getLabelDetails().getDescription().equals("String"); }).collect(Collectors.toList()); Assertions.assertEquals(1, filteredItems.size()); } @@ -495,7 +522,8 @@ void testCompletionForParameterOnCompleteVariableExpression() throws Exception { Assertions.assertTrue(result.isLeft()); List items = result.getLeft(); List filteredItems = items.stream().filter(item -> { - return item.getLabel().equals("paramName") && item.getKind().equals(CompletionItemKind.Variable); + return item.getLabel().equals("paramName") && item.getKind().equals(CompletionItemKind.Variable) && + item.getLabelDetails().getDescription().equals("String"); }).collect(Collectors.toList()); Assertions.assertEquals(1, filteredItems.size()); } @@ -520,7 +548,8 @@ void testCompletionForLocalVariableOnPartialVariableExpression() throws Exceptio Assertions.assertTrue(result.isLeft()); List items = result.getLeft(); List filteredItems = items.stream().filter(item -> { - return item.getLabel().equals("localVar") && item.getKind().equals(CompletionItemKind.Variable); + return item.getLabel().equals("localVar") && item.getKind().equals(CompletionItemKind.Variable) && + item.getLabelDetails().getDescription().equals("String"); }).collect(Collectors.toList()); Assertions.assertEquals(1, filteredItems.size()); } @@ -545,7 +574,8 @@ void testCompletionForLocalVariableOnCompleteVariableExpression() throws Excepti Assertions.assertTrue(result.isLeft()); List items = result.getLeft(); List filteredItems = items.stream().filter(item -> { - return item.getLabel().equals("localVar") && item.getKind().equals(CompletionItemKind.Variable); + return item.getLabel().equals("localVar") && item.getKind().equals(CompletionItemKind.Variable) && + item.getLabelDetails().getDescription().equals("String"); }).collect(Collectors.toList()); Assertions.assertEquals(1, filteredItems.size()); } @@ -572,7 +602,8 @@ void testCompletionForLocalVariableOnPartialVariableExpressionInsideBlock() thro Assertions.assertTrue(result.isLeft()); List items = result.getLeft(); List filteredItems = items.stream().filter(item -> { - return item.getLabel().equals("localVar") && item.getKind().equals(CompletionItemKind.Variable); + return item.getLabel().equals("localVar") && item.getKind().equals(CompletionItemKind.Variable) && + item.getLabelDetails().getDescription().equals("String"); }).collect(Collectors.toList()); Assertions.assertEquals(1, filteredItems.size()); } @@ -598,8 +629,9 @@ void testOwnClass() throws Exception { List items = result.getLeft(); Assertions.assertTrue(items.size() > 0); List filteredItems = items.stream().filter(item -> { - return item.getLabel().equals("Completion") && item.getDetail().equals("com.example") - && item.getKind().equals(CompletionItemKind.Class); + return item.getLabel().equals("Completion") && + item.getLabelDetails().getDescription().equals("com.example") && + item.getKind().equals(CompletionItemKind.Class); }).collect(Collectors.toList()); Assertions.assertEquals(1, filteredItems.size()); } @@ -624,8 +656,9 @@ void testSystemClass() throws Exception { List items = result.getLeft(); Assertions.assertTrue(items.size() > 0); List filteredItems = items.stream().filter(item -> { - return item.getLabel().equals("ArrayList") && item.getDetail().equals("java.util") - && item.getKind().equals(CompletionItemKind.Class); + return item.getLabel().equals("ArrayList") && + item.getLabelDetails().getDescription().equals("java.util") && + item.getKind().equals(CompletionItemKind.Class); }).collect(Collectors.toList()); Assertions.assertEquals(1, filteredItems.size()); }