From db1cedbb688a74a3180b9bfdffdb677b6222d5f8 Mon Sep 17 00:00:00 2001 From: nadeeshaan Date: Tue, 11 Jun 2019 15:17:03 +0530 Subject: [PATCH] Fix Issue #14346 --- .../langserver/common/utils/CommonUtil.java | 83 +++++++-- .../AnnotationAttachmentsCompletionTest.java | 1 + .../annotation/serviceAnnotation1.json | 167 +++++++++--------- .../annotation/serviceAnnotation2.json | 167 +++++++++--------- .../annotation/serviceAnnotation3.json | 8 +- .../annotation/serviceAnnotation4.json | 41 +++++ .../annotation/source/serviceAnnotation4.bal | 10 ++ 7 files changed, 280 insertions(+), 197 deletions(-) create mode 100644 language-server/modules/langserver-core/src/test/resources/completion/annotation/serviceAnnotation4.json create mode 100644 language-server/modules/langserver-core/src/test/resources/completion/annotation/source/serviceAnnotation4.bal diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/common/utils/CommonUtil.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/common/utils/CommonUtil.java index 85c4fa9089b0..85bc9347f73f 100644 --- a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/common/utils/CommonUtil.java +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/common/utils/CommonUtil.java @@ -511,10 +511,22 @@ private static String getAnnotationInsertText(PackageID packageID, BAnnotationSy annotationStart.append(pkgAlias).append(CommonKeys.PKG_DELIMITER_KEYWORD); } if (annotationSymbol.attachedType != null) { - annotationStart.append(annotationSymbol.getName().getValue()).append(" ") - .append(CommonKeys.OPEN_BRACE_KEY).append(LINE_SEPARATOR) - .append("\t").append("${1}").append(LINE_SEPARATOR) - .append(CommonKeys.CLOSE_BRACE_KEY); + annotationStart.append(annotationSymbol.getName().getValue()).append(" ").append(CommonKeys.OPEN_BRACE_KEY) + .append(LINE_SEPARATOR); + List requiredFields = new ArrayList<>(); + if (annotationSymbol.attachedType.type instanceof BRecordType) { + requiredFields = getRecordRequiredFields(((BRecordType) annotationSymbol.attachedType.type)); + List insertTexts = new ArrayList<>(); + requiredFields.forEach(field -> { + String fieldInsertionText = "\t" + getRecordFieldCompletionInsertText(field, 1); + insertTexts.add(fieldInsertionText); + }); + annotationStart.append(String.join("," + LINE_SEPARATOR, insertTexts)); + } + if (requiredFields.isEmpty()) { + annotationStart.append("\t").append("${1}").append(LINE_SEPARATOR); + } + annotationStart.append(CommonKeys.CLOSE_BRACE_KEY); } else { annotationStart.append(annotationSymbol.getName().getValue()); } @@ -641,22 +653,9 @@ public static boolean listContainsPackage(String pkg, List pkg public static List getRecordFieldCompletionItems(List fields) { List completionItems = new ArrayList<>(); fields.forEach(field -> { - BType fieldType = field.getType(); - StringBuilder insertText = new StringBuilder(field.getName().getValue() + ": "); - if (fieldType instanceof BRecordType) { - insertText.append("{").append(LINE_SEPARATOR).append("\t${1}").append(LINE_SEPARATOR).append("}"); - } else if (fieldType instanceof BArrayType) { - insertText.append("[").append("${1}").append("]"); - } else if (fieldType.tsymbol != null && fieldType.tsymbol.name.getValue().equals("string")) { - insertText.append("\"").append("${1}").append("\""); - } else { - insertText.append("${1:").append(getDefaultValueForType(field.getType())).append("}"); - if (field.getType() instanceof BFiniteType || field.getType() instanceof BUnionType) { - insertText.append(getFiniteAndUnionTypesComment(field.getType())); - } - } + String insertText = getRecordFieldCompletionInsertText(field, 0); CompletionItem fieldItem = new CompletionItem(); - fieldItem.setInsertText(insertText.toString()); + fieldItem.setInsertText(insertText); fieldItem.setInsertTextFormat(InsertTextFormat.Snippet); fieldItem.setLabel(field.getName().getValue()); fieldItem.setDetail(ItemResolverConstants.FIELD_TYPE); @@ -976,6 +975,52 @@ public static BallerinaParser prepareParser(String content, boolean removeErrorL return new BallerinaParser(commonTokenStream); } + + private static List getRecordRequiredFields(BRecordType recordType) { + return recordType.fields.stream() + .filter(field -> (field.symbol.flags & Flags.REQUIRED) == Flags.REQUIRED) + .collect(Collectors.toList()); + } + + /** + * Get the completion item insert text for a BField. + * + * @param bField BField to evaluate + * @return {@link String} Insert text + */ + private static String getRecordFieldCompletionInsertText(BField bField, int tabOffset) { + BType fieldType = bField.getType(); + StringBuilder insertText = new StringBuilder(bField.getName().getValue() + ": "); + if (fieldType instanceof BRecordType) { + List requiredFields = getRecordRequiredFields((BRecordType) fieldType); + if (requiredFields.isEmpty()) { + insertText.append("{").append("${1}}"); + return insertText.toString(); + } + insertText.append("{").append(LINE_SEPARATOR); + int tabCount = tabOffset; + for (BField requiredField : requiredFields) { + insertText.append(String.join("", Collections.nCopies(tabCount + 1, "\t"))) + .append(getRecordFieldCompletionInsertText(requiredField, tabCount)) + .append(String.join("", Collections.nCopies(tabCount, "\t"))) + .append(LINE_SEPARATOR); + tabCount++; + } + insertText.append(String.join("", Collections.nCopies(tabOffset, "\t"))) + .append("}").append(LINE_SEPARATOR); + } else if (fieldType instanceof BArrayType) { + insertText.append("[").append("${1}").append("]"); + } else if (fieldType.tsymbol != null && fieldType.tsymbol.name.getValue().equals("string")) { + insertText.append("\"").append("${1}").append("\""); + } else { + insertText.append("${1:").append(getDefaultValueForType(bField.getType())).append("}"); + if (bField.getType() instanceof BFiniteType || bField.getType() instanceof BUnionType) { + insertText.append(getFiniteAndUnionTypesComment(bField.getType())); + } + } + + return insertText.toString(); + } private static SymbolInfo getIterableOpSymbolInfo(SnippetBlock operation, @Nullable BType bType, String label, LSContext context) { diff --git a/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/completion/annotations/AnnotationAttachmentsCompletionTest.java b/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/completion/annotations/AnnotationAttachmentsCompletionTest.java index 44ed6e498176..6f9cdf292fd3 100644 --- a/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/completion/annotations/AnnotationAttachmentsCompletionTest.java +++ b/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/completion/annotations/AnnotationAttachmentsCompletionTest.java @@ -44,6 +44,7 @@ public Object[][] dataProvider() { {"serviceAnnotation1.json", "annotation"}, {"serviceAnnotation2.json", "annotation"}, {"serviceAnnotation3.json", "annotation"}, + {"serviceAnnotation4.json", "annotation"}, {"resourceAnnotation1.json", "annotation"}, {"resourceAnnotation2.json", "annotation"}, {"functionAnnotation1.json", "annotation"}, diff --git a/language-server/modules/langserver-core/src/test/resources/completion/annotation/serviceAnnotation1.json b/language-server/modules/langserver-core/src/test/resources/completion/annotation/serviceAnnotation1.json index 4b5c4677ebd7..877c1e8bcf10 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/annotation/serviceAnnotation1.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/annotation/serviceAnnotation1.json @@ -6,135 +6,128 @@ "source": "annotation/source/serviceAnnotation1.bal", "items": [ { - "label":"openapi:ServiceInfo", - "kind":"Property", - "detail":"Annotation", - "sortText":"110", - "insertText":"openapi:ServiceInfo {\n\t${1}\n}", - "insertTextFormat":"Snippet", - "additionalTextEdits":[ + "label": "openapi:ServiceInfo", + "kind": "Property", + "detail": "Annotation", + "insertText": "openapi:ServiceInfo {\n\t${1}\n}", + "insertTextFormat": "Snippet", + "additionalTextEdits": [ { - "range":{ - "start":{ - "line":1, - "character":0 + "range": { + "start": { + "line": 1, + "character": 0 }, - "end":{ - "line":1, - "character":0 + "end": { + "line": 1, + "character": 0 } }, - "newText":"import ballerina/openapi;\n" + "newText": "import ballerina/openapi;\n" } ] }, { - "label":"openapi:ClientConfig", - "kind":"Property", - "detail":"Annotation", - "sortText":"110", - "insertText":"openapi:ClientConfig {\n\t${1}\n}", - "insertTextFormat":"Snippet", - "additionalTextEdits":[ + "label": "openapi:ClientConfig", + "kind": "Property", + "detail": "Annotation", + "insertText": "openapi:ClientConfig {\n\t${1}\n}", + "insertTextFormat": "Snippet", + "additionalTextEdits": [ { - "range":{ - "start":{ - "line":1, - "character":0 + "range": { + "start": { + "line": 1, + "character": 0 }, - "end":{ - "line":1, - "character":0 + "end": { + "line": 1, + "character": 0 } }, - "newText":"import ballerina/openapi;\n" + "newText": "import ballerina/openapi;\n" } ] }, { - "label":"websub:SubscriberServiceConfig", - "kind":"Property", - "detail":"Annotation", - "sortText":"110", - "insertText":"websub:SubscriberServiceConfig {\n\t${1}\n}", - "insertTextFormat":"Snippet", - "additionalTextEdits":[ + "label": "artemis:ServiceConfig", + "kind": "Property", + "detail": "Annotation", + "insertText": "artemis:ServiceConfig {\n\tqueueConfig: {\n\t\tqueueName: \"${1}\"\t\n\t}\n}", + "insertTextFormat": "Snippet", + "additionalTextEdits": [ { - "range":{ - "start":{ - "line":1, - "character":0 + "range": { + "start": { + "line": 1, + "character": 0 }, - "end":{ - "line":1, - "character":0 + "end": { + "line": 1, + "character": 0 } }, - "newText":"import ballerina/websub;\n" + "newText": "import ballerina/artemis;\n" } ] }, { - "label":"grpc:ServiceConfig", - "kind":"Property", - "detail":"Annotation", - "sortText":"110", - "insertText":"grpc:ServiceConfig {\n\t${1}\n}", - "insertTextFormat":"Snippet", - "additionalTextEdits":[ + "label": "nats:ConsumerConfig", + "kind": "Property", + "detail": "Annotation", + "insertText": "nats:ConsumerConfig {\n\tsubject: \"${1}\"}", + "insertTextFormat": "Snippet", + "additionalTextEdits": [ { - "range":{ - "start":{ - "line":1, - "character":0 + "range": { + "start": { + "line": 1, + "character": 0 }, - "end":{ - "line":1, - "character":0 + "end": { + "line": 1, + "character": 0 } }, - "newText":"import ballerina/grpc;\n" + "newText": "import ballerina/nats;\n" } ] }, { - "label":"grpc:ServiceDescriptor", - "kind":"Property", - "detail":"Annotation", - "sortText":"110", - "insertText":"grpc:ServiceDescriptor {\n\t${1}\n}", - "insertTextFormat":"Snippet", - "additionalTextEdits":[ + "label": "rabbitmq:ServiceConfig", + "kind": "Property", + "detail": "Annotation", + "insertText": "rabbitmq:ServiceConfig {\n\tqueueConfig: {\n\t\tqueueName: \"${1}\"\t\n\t}\n}", + "insertTextFormat": "Snippet", + "additionalTextEdits": [ { - "range":{ - "start":{ - "line":1, - "character":0 + "range": { + "start": { + "line": 1, + "character": 0 }, - "end":{ - "line":1, - "character":0 + "end": { + "line": 1, + "character": 0 } }, - "newText":"import ballerina/grpc;\n" + "newText": "import ballerina/rabbitmq;\n" } ] }, { - "label":"http:ServiceConfig", - "kind":"Property", - "detail":"Annotation", - "sortText":"110", - "insertText":"http:ServiceConfig {\n\t${1}\n}", - "insertTextFormat":"Snippet" + "label": "http:ServiceConfig", + "kind": "Property", + "detail": "Annotation", + "insertText": "http:ServiceConfig {\n\t${1}\n}", + "insertTextFormat": "Snippet" }, { - "label":"http:WebSocketServiceConfig", - "kind":"Property", - "detail":"Annotation", - "sortText":"110", - "insertText":"http:WebSocketServiceConfig {\n\t${1}\n}", - "insertTextFormat":"Snippet" + "label": "http:WebSocketServiceConfig", + "kind": "Property", + "detail": "Annotation", + "insertText": "http:WebSocketServiceConfig {\n\t${1}\n}", + "insertTextFormat": "Snippet" } ] } \ No newline at end of file diff --git a/language-server/modules/langserver-core/src/test/resources/completion/annotation/serviceAnnotation2.json b/language-server/modules/langserver-core/src/test/resources/completion/annotation/serviceAnnotation2.json index 83df8492ad6d..00e2e50dec4a 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/annotation/serviceAnnotation2.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/annotation/serviceAnnotation2.json @@ -6,135 +6,128 @@ "source": "annotation/source/serviceAnnotation2.bal", "items": [ { - "label":"openapi:ServiceInfo", - "kind":"Property", - "detail":"Annotation", - "sortText":"110", - "insertText":"openapi:ServiceInfo {\n\t${1}\n}", - "insertTextFormat":"Snippet", - "additionalTextEdits":[ + "label": "openapi:ServiceInfo", + "kind": "Property", + "detail": "Annotation", + "insertText": "openapi:ServiceInfo {\n\t${1}\n}", + "insertTextFormat": "Snippet", + "additionalTextEdits": [ { - "range":{ - "start":{ - "line":1, - "character":0 + "range": { + "start": { + "line": 1, + "character": 0 }, - "end":{ - "line":1, - "character":0 + "end": { + "line": 1, + "character": 0 } }, - "newText":"import ballerina/openapi;\n" + "newText": "import ballerina/openapi;\n" } ] }, { - "label":"openapi:ClientConfig", - "kind":"Property", - "detail":"Annotation", - "sortText":"110", - "insertText":"openapi:ClientConfig {\n\t${1}\n}", - "insertTextFormat":"Snippet", - "additionalTextEdits":[ + "label": "openapi:ClientConfig", + "kind": "Property", + "detail": "Annotation", + "insertText": "openapi:ClientConfig {\n\t${1}\n}", + "insertTextFormat": "Snippet", + "additionalTextEdits": [ { - "range":{ - "start":{ - "line":1, - "character":0 + "range": { + "start": { + "line": 1, + "character": 0 }, - "end":{ - "line":1, - "character":0 + "end": { + "line": 1, + "character": 0 } }, - "newText":"import ballerina/openapi;\n" + "newText": "import ballerina/openapi;\n" } ] }, { - "label":"websub:SubscriberServiceConfig", - "kind":"Property", - "detail":"Annotation", - "sortText":"110", - "insertText":"websub:SubscriberServiceConfig {\n\t${1}\n}", - "insertTextFormat":"Snippet", - "additionalTextEdits":[ + "label": "artemis:ServiceConfig", + "kind": "Property", + "detail": "Annotation", + "insertText": "artemis:ServiceConfig {\n\tqueueConfig: {\n\t\tqueueName: \"${1}\"\t\n\t}\n}", + "insertTextFormat": "Snippet", + "additionalTextEdits": [ { - "range":{ - "start":{ - "line":1, - "character":0 + "range": { + "start": { + "line": 1, + "character": 0 }, - "end":{ - "line":1, - "character":0 + "end": { + "line": 1, + "character": 0 } }, - "newText":"import ballerina/websub;\n" + "newText": "import ballerina/artemis;\n" } ] }, { - "label":"grpc:ServiceConfig", - "kind":"Property", - "detail":"Annotation", - "sortText":"110", - "insertText":"grpc:ServiceConfig {\n\t${1}\n}", - "insertTextFormat":"Snippet", - "additionalTextEdits":[ + "label": "nats:ConsumerConfig", + "kind": "Property", + "detail": "Annotation", + "insertText": "nats:ConsumerConfig {\n\tsubject: \"${1}\"}", + "insertTextFormat": "Snippet", + "additionalTextEdits": [ { - "range":{ - "start":{ - "line":1, - "character":0 + "range": { + "start": { + "line": 1, + "character": 0 }, - "end":{ - "line":1, - "character":0 + "end": { + "line": 1, + "character": 0 } }, - "newText":"import ballerina/grpc;\n" + "newText": "import ballerina/nats;\n" } ] }, { - "label":"grpc:ServiceDescriptor", - "kind":"Property", - "detail":"Annotation", - "sortText":"110", - "insertText":"grpc:ServiceDescriptor {\n\t${1}\n}", - "insertTextFormat":"Snippet", - "additionalTextEdits":[ + "label": "rabbitmq:ServiceConfig", + "kind": "Property", + "detail": "Annotation", + "insertText": "rabbitmq:ServiceConfig {\n\tqueueConfig: {\n\t\tqueueName: \"${1}\"\t\n\t}\n}", + "insertTextFormat": "Snippet", + "additionalTextEdits": [ { - "range":{ - "start":{ - "line":1, - "character":0 + "range": { + "start": { + "line": 1, + "character": 0 }, - "end":{ - "line":1, - "character":0 + "end": { + "line": 1, + "character": 0 } }, - "newText":"import ballerina/grpc;\n" + "newText": "import ballerina/rabbitmq;\n" } ] }, { - "label":"http:ServiceConfig", - "kind":"Property", - "detail":"Annotation", - "sortText":"110", - "insertText":"http:ServiceConfig {\n\t${1}\n}", - "insertTextFormat":"Snippet" + "label": "http:ServiceConfig", + "kind": "Property", + "detail": "Annotation", + "insertText": "http:ServiceConfig {\n\t${1}\n}", + "insertTextFormat": "Snippet" }, { - "label":"http:WebSocketServiceConfig", - "kind":"Property", - "detail":"Annotation", - "sortText":"110", - "insertText":"http:WebSocketServiceConfig {\n\t${1}\n}", - "insertTextFormat":"Snippet" + "label": "http:WebSocketServiceConfig", + "kind": "Property", + "detail": "Annotation", + "insertText": "http:WebSocketServiceConfig {\n\t${1}\n}", + "insertTextFormat": "Snippet" } ] } \ No newline at end of file diff --git a/language-server/modules/langserver-core/src/test/resources/completion/annotation/serviceAnnotation3.json b/language-server/modules/langserver-core/src/test/resources/completion/annotation/serviceAnnotation3.json index b826d31d614e..4f63a79ce5ac 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/annotation/serviceAnnotation3.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/annotation/serviceAnnotation3.json @@ -34,7 +34,7 @@ "kind": "Field", "detail": "Field", "sortText": "120", - "insertText": "compression: {\n\t${1}\n}", + "insertText": "compression: {${1}}", "insertTextFormat": "Snippet" }, { @@ -50,7 +50,7 @@ "kind": "Field", "detail": "Field", "sortText": "120", - "insertText": "cors: {\n\t${1}\n}", + "insertText": "cors: {${1}}", "insertTextFormat": "Snippet" }, { @@ -58,7 +58,7 @@ "kind": "Field", "detail": "Field", "sortText": "120", - "insertText": "versioning: {\n\t${1}\n}", + "insertText": "versioning: {${1}}", "insertTextFormat": "Snippet" }, { @@ -66,7 +66,7 @@ "kind": "Field", "detail": "Field", "sortText": "120", - "insertText": "auth: {\n\t${1}\n}", + "insertText": "auth: {${1}}", "insertTextFormat": "Snippet" }, { diff --git a/language-server/modules/langserver-core/src/test/resources/completion/annotation/serviceAnnotation4.json b/language-server/modules/langserver-core/src/test/resources/completion/annotation/serviceAnnotation4.json new file mode 100644 index 000000000000..ebf85c25caf3 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/completion/annotation/serviceAnnotation4.json @@ -0,0 +1,41 @@ +{ + "position": { + "line": 3, + "character": 4 + }, + "source": "annotation/source/serviceAnnotation4.bal", + "items": [ + { + "label": "autoAck", + "kind": "Field", + "detail": "Field", + "sortText": "120", + "insertText": "autoAck: ${1:false}", + "insertTextFormat": "Snippet" + }, + { + "label": "queueConfig", + "kind": "Field", + "detail": "Field", + "sortText": "120", + "insertText": "queueConfig: {\n\tqueueName: \"${1}\"\n}\n", + "insertTextFormat": "Snippet" + }, + { + "label": "filter", + "kind": "Field", + "detail": "Field", + "sortText": "120", + "insertText": "filter: ${1:\"\"} // Values allowed: string|()", + "insertTextFormat": "Snippet" + }, + { + "label": "Add All Attributes", + "kind": "Property", + "detail": "none", + "sortText": "110", + "insertText": "autoAck: false,\nqueueConfig: {},\nfilter: \"\" // Values allowed: string|()", + "insertTextFormat": "Snippet" + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/completion/annotation/source/serviceAnnotation4.bal b/language-server/modules/langserver-core/src/test/resources/completion/annotation/source/serviceAnnotation4.bal new file mode 100644 index 000000000000..75a9f568bfc1 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/completion/annotation/source/serviceAnnotation4.bal @@ -0,0 +1,10 @@ +import ballerina/artemis; + +@artemis:ServiceConfig { + +} +service helloService on new http:Listener(8080) { + resource function sayHello(http:Caller caller, http:Request request) { + + } +} \ No newline at end of file