diff --git a/compiler/ballerina-parser/src/test/resources/declarations/service-decl/service_decl_assert_23.json b/compiler/ballerina-parser/src/test/resources/declarations/service-decl/service_decl_assert_23.json index 787cf674f9b4..1dccb2ffcedc 100644 --- a/compiler/ballerina-parser/src/test/resources/declarations/service-decl/service_decl_assert_23.json +++ b/compiler/ballerina-parser/src/test/resources/declarations/service-decl/service_decl_assert_23.json @@ -292,6 +292,10 @@ { "kind": "RESOURCE_KEYWORD", "leadingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + }, { "kind": "WHITESPACE_MINUTIAE", "value": " " diff --git a/compiler/ballerina-parser/src/test/resources/declarations/service-decl/service_decl_source_23.bal b/compiler/ballerina-parser/src/test/resources/declarations/service-decl/service_decl_source_23.bal index 874cd6bed058..ead111814d1f 100644 --- a/compiler/ballerina-parser/src/test/resources/declarations/service-decl/service_decl_source_23.bal +++ b/compiler/ballerina-parser/src/test/resources/declarations/service-decl/service_decl_source_23.bal @@ -1,6 +1,7 @@ service / on new http:Listener(8080) { resource function get /hello(string name) { } + resource function get hello/(string name) { } } diff --git a/misc/formatter/modules/formatter-core/src/main/java/org/ballerinalang/formatter/core/FormattingTreeModifier.java b/misc/formatter/modules/formatter-core/src/main/java/org/ballerinalang/formatter/core/FormattingTreeModifier.java index daf03770d9c9..1dbb9f71852c 100644 --- a/misc/formatter/modules/formatter-core/src/main/java/org/ballerinalang/formatter/core/FormattingTreeModifier.java +++ b/misc/formatter/modules/formatter-core/src/main/java/org/ballerinalang/formatter/core/FormattingTreeModifier.java @@ -253,6 +253,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.function.Predicate; import java.util.stream.Collectors; import static org.ballerinalang.formatter.core.FormatterUtils.isInlineRange; @@ -288,7 +289,8 @@ public FormattingTreeModifier(FormattingOptions options, LineRange lineRange) { @Override public ModulePartNode transform(ModulePartNode modulePartNode) { NodeList imports = sortAndGroupImportDeclarationNodes(modulePartNode.imports()); - NodeList members = formatModuleMembers(modulePartNode.members()); + NodeList members = + formatMemberDeclarations(modulePartNode.members(), n -> isMultilineModuleMember(n)); Token eofToken = formatToken(modulePartNode.eofToken(), 0, 0); return modulePartNode.modify(imports, members, eofToken); } @@ -700,7 +702,8 @@ public ServiceDeclarationNode transform(ServiceDeclarationNode serviceDeclaratio formatSeparatedNodeList(serviceDeclarationNode.expressions(), 0, 0, 1, 0); Token openBrace = formatToken(serviceDeclarationNode.openBraceToken(), 0, 1); indent(); // increase the indentation of the following statements. - NodeList members = formatNodeList(serviceDeclarationNode.members(), 0, 1, 0, 1); + NodeList members = + formatMemberDeclarations(serviceDeclarationNode.members(), n -> isClassOrServiceMultiLineMember(n)); unindent(); // reset the indentation. Optional optSemicolon = serviceDeclarationNode.semicolonToken(); Token closeBrace = optSemicolon.isPresent() ? @@ -3462,7 +3465,8 @@ public ClassDefinitionNode transform(ClassDefinitionNode classDefinitionNode) { Token openBrace = formatToken(classDefinitionNode.openBrace(), 0, 1); indent(); - NodeList members = formatNodeList(classDefinitionNode.members(), 0, 1, 0, 1); + NodeList members = + formatMemberDeclarations(classDefinitionNode.members(), n -> isClassOrServiceMultiLineMember(n)); unindent(); Optional optSemicolon = classDefinitionNode.semicolonToken(); Token closeBrace = optSemicolon.isPresent() ? @@ -3837,7 +3841,14 @@ private T formatToken(T token, int trailingWS, int trailingNL) return formatToken(token, trailingWS, trailingNL, null); } - protected NodeList formatModuleMembers(NodeList members) { + /** + * Format members in module-level, class and service level. + * + * @param members Members of the scope + * @param filter filter to identify multiline members + * @return Formatted list of members + */ + protected NodeList formatMemberDeclarations(NodeList members, Predicate filter) { if (members.isEmpty()) { return members; } @@ -3855,7 +3866,7 @@ protected NodeList formatModuleMembers(NodeList members) // We need to do this check, because different kinds of children needs // different number of newlines in-between. int itemTrailingNL = 1; - if (isMultilineModuleMember(currentMember) || isMultilineModuleMember(nextMember)) { + if (filter.test(currentMember) || filter.test(nextMember)) { itemTrailingNL++; } @@ -3896,6 +3907,20 @@ private boolean isMultilineModuleMember(T node) { } } + private boolean isClassOrServiceMultiLineMember(Node node) { + if (node == null) { + return false; + } + + switch (node.kind()) { + case OBJECT_METHOD_DEFINITION: + case RESOURCE_ACCESSOR_DEFINITION: + return true; + default: + return false; + } + } + /** * Format a list of nodes. * diff --git a/misc/formatter/modules/formatter-core/src/test/resources/declarations/class-definition/assert/class_definition_declaration_17.bal b/misc/formatter/modules/formatter-core/src/test/resources/declarations/class-definition/assert/class_definition_declaration_17.bal index 80012ec6962c..22246c22cba3 100644 --- a/misc/formatter/modules/formatter-core/src/test/resources/declarations/class-definition/assert/class_definition_declaration_17.bal +++ b/misc/formatter/modules/formatter-core/src/test/resources/declarations/class-definition/assert/class_definition_declaration_17.bal @@ -6,6 +6,7 @@ class Foo { function getName() { } + remote function get() { } } diff --git a/misc/formatter/modules/formatter-core/src/test/resources/declarations/class-definition/assert/class_definition_declaration_18.bal b/misc/formatter/modules/formatter-core/src/test/resources/declarations/class-definition/assert/class_definition_declaration_18.bal new file mode 100644 index 000000000000..9ca5e840e594 --- /dev/null +++ b/misc/formatter/modules/formatter-core/src/test/resources/declarations/class-definition/assert/class_definition_declaration_18.bal @@ -0,0 +1,17 @@ +class Student { + string name; + int age; + + public function init(string name, int age) { + self.name = name; + self.age = age; + } + + public function getName() returns string { + return self.name; + } + + public function getAge() returns int { + return self.age; + } +} diff --git a/misc/formatter/modules/formatter-core/src/test/resources/declarations/class-definition/assert/class_definition_declaration_2.bal b/misc/formatter/modules/formatter-core/src/test/resources/declarations/class-definition/assert/class_definition_declaration_2.bal index e76944a8caf5..73abf4af0e3f 100644 --- a/misc/formatter/modules/formatter-core/src/test/resources/declarations/class-definition/assert/class_definition_declaration_2.bal +++ b/misc/formatter/modules/formatter-core/src/test/resources/declarations/class-definition/assert/class_definition_declaration_2.bal @@ -10,9 +10,11 @@ class Person { public string name = "sample name"; int year = 50; + function bar() returns string { string b = "bar"; return b; } + string month = "february"; } diff --git a/misc/formatter/modules/formatter-core/src/test/resources/declarations/class-definition/source/class_definition_declaration_18.bal b/misc/formatter/modules/formatter-core/src/test/resources/declarations/class-definition/source/class_definition_declaration_18.bal new file mode 100644 index 000000000000..1a2ab611e0f3 --- /dev/null +++ b/misc/formatter/modules/formatter-core/src/test/resources/declarations/class-definition/source/class_definition_declaration_18.bal @@ -0,0 +1,18 @@ +class Student { + string name; + int age; + + public function init(string name, int age) { + self.name = name; + self.age = age; + } + public function getName() returns string { + return self.name; + } + + + + public function getAge() returns int { + return self.age; + } +} diff --git a/misc/formatter/modules/formatter-core/src/test/resources/declarations/service-listener/assert/service_listener_declaration_3.bal b/misc/formatter/modules/formatter-core/src/test/resources/declarations/service-listener/assert/service_listener_declaration_3.bal new file mode 100644 index 000000000000..d2e105906ee8 --- /dev/null +++ b/misc/formatter/modules/formatter-core/src/test/resources/declarations/service-listener/assert/service_listener_declaration_3.bal @@ -0,0 +1,26 @@ +import ballerina/http; + +public type Student readonly & record { + string name; + int age; +}; + +http:Listener httpListener = check new (9090); + +public service class StudentService { + *http:Service; + + Student[] students = []; + + resource function get students() returns Student[] { + return self.students; + } + + resource function post student(Student student) { + self.students.push(student); + } + + resource function post students(Student[] students) { + self.students.push(...students); + } +} diff --git a/misc/formatter/modules/formatter-core/src/test/resources/declarations/service-listener/assert/service_listener_declaration_4.bal b/misc/formatter/modules/formatter-core/src/test/resources/declarations/service-listener/assert/service_listener_declaration_4.bal new file mode 100644 index 000000000000..13c199529d55 --- /dev/null +++ b/misc/formatter/modules/formatter-core/src/test/resources/declarations/service-listener/assert/service_listener_declaration_4.bal @@ -0,0 +1,35 @@ +import ballerina/grpc; + +configurable int port = 9090; + +type Book record { + string id; + string title; + string author; + float price; +}; + +Book[] books = [ + {id: "1", title: "Book One", author: "Author One", price: 29.99}, + {id: "2", title: "Book Two", author: "Author Two", price: 19.99}, + {id: "3", title: "Book Three", author: "Author Three", price: 39.99} +]; + +service "Books" on new grpc:Listener(port) { + remote function addBook(Book book) returns Book|error { + books.push(book); + return book; + } + + remote function getBook(string id) returns Book|error { + Book[] book = books.filter(b => b.id == id); + if (book.length() == 0) { + return error grpc:NotFoundError(string `Cannot find the album for ID ${id}`); + } + return book[0]; + } + + remote function listBooks() returns stream|error { + return books.toStream(); + } +} diff --git a/misc/formatter/modules/formatter-core/src/test/resources/declarations/service-listener/source/service_listener_declaration_3.bal b/misc/formatter/modules/formatter-core/src/test/resources/declarations/service-listener/source/service_listener_declaration_3.bal new file mode 100644 index 000000000000..8bff4c1a05ab --- /dev/null +++ b/misc/formatter/modules/formatter-core/src/test/resources/declarations/service-listener/source/service_listener_declaration_3.bal @@ -0,0 +1,27 @@ +import ballerina/http; + +public type Student readonly & record { + string name; + int age; +}; + +http:Listener httpListener = check new (9090); + +public service class StudentService { + *http:Service; + + Student[] students = []; + + resource function get students() returns Student[] { + return self.students; + } + resource function post student(Student student) { + self.students.push(student); + } + + + + resource function post students(Student[] students) { + self.students.push(...students); + } +} diff --git a/misc/formatter/modules/formatter-core/src/test/resources/declarations/service-listener/source/service_listener_declaration_4.bal b/misc/formatter/modules/formatter-core/src/test/resources/declarations/service-listener/source/service_listener_declaration_4.bal new file mode 100644 index 000000000000..06d3b9d3ab00 --- /dev/null +++ b/misc/formatter/modules/formatter-core/src/test/resources/declarations/service-listener/source/service_listener_declaration_4.bal @@ -0,0 +1,37 @@ +import ballerina/grpc; + + +configurable int port = 9090; + +type Book record { + string id; + string title; + string author; + float price; +}; + +Book[] books = [ + {id: "1", title: "Book One", author: "Author One", price: 29.99}, + {id: "2", title: "Book Two", author: "Author Two", price: 19.99}, + {id: "3", title: "Book Three", author: "Author Three", price: 39.99} + ]; + +service "Books" on new grpc:Listener(port) { + remote function addBook(Book book) returns Book | error { + books.push(book); + return book; + } + remote function getBook(string id) returns Book | error { + Book[] book = books.filter(b => b.id == id); + if (book.length() == 0) { + return error grpc:NotFoundError(string `Cannot find the album for ID ${id}`); + } + return book[0]; + } + + + + remote function listBooks( ) returns stream | error { + return books.toStream(); + } +}