From 59c20edc3b22f286867e78f0cf8ba05a1a7f1c0f Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Thu, 28 Apr 2016 09:12:13 -0700 Subject: add comments from .proto file to generated C# files --- src/compiler/csharp_generator.cc | 89 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 82 insertions(+), 7 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/csharp_generator.cc b/src/compiler/csharp_generator.cc index 0d1404341d..ac0fee1ec4 100644 --- a/src/compiler/csharp_generator.cc +++ b/src/compiler/csharp_generator.cc @@ -52,6 +52,7 @@ using grpc::protobuf::MethodDescriptor; using grpc::protobuf::io::Printer; using grpc::protobuf::io::StringOutputStream; using grpc_generator::MethodType; +using grpc_generator::GetCppComments; using grpc_generator::GetMethodType; using grpc_generator::METHODTYPE_NO_STREAMING; using grpc_generator::METHODTYPE_CLIENT_STREAMING; @@ -65,6 +66,56 @@ using std::vector; namespace grpc_csharp_generator { namespace { +// This function is a massaged version of +// https://github.com/google/protobuf/blob/master/src/google/protobuf/compiler/csharp/csharp_doc_comment.cc +// Currently, we cannot easily reuse the functionality as +// google/protobuf/compiler/csharp/csharp_doc_comment.h is not a public header. +// TODO(jtattermusch): reuse the functionality from google/protobuf. +void GenerateDocCommentBodyImpl(grpc::protobuf::io::Printer* printer, grpc::protobuf::SourceLocation location) { + grpc::string comments = location.leading_comments.empty() ? + location.trailing_comments : location.leading_comments; + if (comments.empty()) { + return; + } + // XML escaping... no need for apostrophes etc as the whole text is going to be a child + // node of a summary element, not part of an attribute. + comments = grpc_generator::StringReplace(comments, "&", "&", true); + comments = grpc_generator::StringReplace(comments, "<", "<", true); + + std::vector lines; + grpc_generator::Split(comments, '\n', &lines); + // TODO: We really should work out which part to put in the summary and which to put in the remarks... + // but that needs to be part of a bigger effort to understand the markdown better anyway. + printer->Print("/// \n"); + bool last_was_empty = false; + // We squash multiple blank lines down to one, and remove any trailing blank lines. We need + // to preserve the blank lines themselves, as this is relevant in the markdown. + // Note that we can't remove leading or trailing whitespace as *that's* relevant in markdown too. + // (We don't skip "just whitespace" lines, either.) + for (std::vector::iterator it = lines.begin(); it != lines.end(); ++it) { + grpc::string line = *it; + if (line.empty()) { + last_was_empty = true; + } else { + if (last_was_empty) { + printer->Print("///\n"); + } + last_was_empty = false; + printer->Print("/// $line$\n", "line", *it); + } + } + printer->Print("/// \n"); +} + +template +void GenerateDocCommentBody( + grpc::protobuf::io::Printer* printer, const DescriptorType* descriptor) { + grpc::protobuf::SourceLocation location; + if (descriptor->GetSourceLocation(&location)) { + GenerateDocCommentBodyImpl(printer, location); + } +} + std::string GetServiceClassName(const ServiceDescriptor* service) { return service->name(); } @@ -242,7 +293,7 @@ void GenerateStaticMethodField(Printer* out, const MethodDescriptor *method) { void GenerateServiceDescriptorProperty(Printer* out, const ServiceDescriptor *service) { std::ostringstream index; index << service->index(); - out->Print("// service descriptor\n"); + out->Print("/// Service descriptor\n"); out->Print("public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor\n"); out->Print("{\n"); out->Print(" get { return $umbrella$.Descriptor.Services[$index$]; }\n", @@ -253,7 +304,8 @@ void GenerateServiceDescriptorProperty(Printer* out, const ServiceDescriptor *se } void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) { - out->Print("// client interface\n"); + out->Print("/// Client for $servicename$\n", + "servicename", GetServiceClassName(service)); out->Print("[System.Obsolete(\"Client side interfaced will be removed " "in the next release. Use client class directly.\")]\n"); out->Print("public interface $name$\n", "name", @@ -266,6 +318,7 @@ void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) { if (method_type == METHODTYPE_NO_STREAMING) { // unary calls have an extra synchronous stub method + GenerateDocCommentBody(out, method); out->Print( "$response$ $methodname$($request$ request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));\n", "methodname", method->name(), "request", @@ -273,6 +326,7 @@ void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) { GetClassName(method->output_type())); // overload taking CallOptions as a param + GenerateDocCommentBody(out, method); out->Print( "$response$ $methodname$($request$ request, CallOptions options);\n", "methodname", method->name(), "request", @@ -284,6 +338,7 @@ void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) { if (method_type == METHODTYPE_NO_STREAMING) { method_name += "Async"; // prevent name clash with synchronous method. } + GenerateDocCommentBody(out, method); out->Print( "$returntype$ $methodname$($request_maybe$Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));\n", "methodname", method_name, "request_maybe", @@ -291,6 +346,7 @@ void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) { GetMethodReturnTypeClient(method)); // overload taking CallOptions as a param + GenerateDocCommentBody(out, method); out->Print( "$returntype$ $methodname$($request_maybe$CallOptions options);\n", "methodname", method_name, "request_maybe", @@ -303,7 +359,8 @@ void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) { } void GenerateServerInterface(Printer* out, const ServiceDescriptor *service) { - out->Print("// server-side interface\n"); + out->Print("/// Interface of server-side implementations of $servicename$\n", + "servicename", GetServiceClassName(service)); out->Print("[System.Obsolete(\"Service implementations should inherit" " from the generated abstract base class instead.\")]\n"); out->Print("public interface $name$\n", "name", @@ -312,6 +369,7 @@ void GenerateServerInterface(Printer* out, const ServiceDescriptor *service) { out->Indent(); for (int i = 0; i < service->method_count(); i++) { const MethodDescriptor *method = service->method(i); + GenerateDocCommentBody(out, method); out->Print( "$returntype$ $methodname$($request$$response_stream_maybe$, " "ServerCallContext context);\n", @@ -326,13 +384,15 @@ void GenerateServerInterface(Printer* out, const ServiceDescriptor *service) { } void GenerateServerClass(Printer* out, const ServiceDescriptor *service) { - out->Print("// server-side abstract class\n"); + out->Print("/// Base class for server-side implementations of $servicename$\n", + "servicename", GetServiceClassName(service)); out->Print("public abstract class $name$\n", "name", GetServerClassName(service)); out->Print("{\n"); out->Indent(); for (int i = 0; i < service->method_count(); i++) { const MethodDescriptor *method = service->method(i); + GenerateDocCommentBody(out, method); out->Print( "public virtual $returntype$ $methodname$($request$$response_stream_maybe$, " "ServerCallContext context)\n", @@ -353,7 +413,8 @@ void GenerateServerClass(Printer* out, const ServiceDescriptor *service) { } void GenerateClientStub(Printer* out, const ServiceDescriptor *service) { - out->Print("// client stub\n"); + out->Print("/// Client for $servicename$\n", + "servicename", GetServiceClassName(service)); out->Print("#pragma warning disable 0618\n"); out->Print( "public class $name$ : ClientBase<$name$>, $interface$\n", @@ -392,6 +453,7 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) { if (method_type == METHODTYPE_NO_STREAMING) { // unary calls have an extra synchronous stub method + GenerateDocCommentBody(out, method); out->Print("public virtual $response$ $methodname$($request$ request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))\n", "methodname", method->name(), "request", GetClassName(method->input_type()), "response", @@ -404,6 +466,7 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) { out->Print("}\n"); // overload taking CallOptions as a param + GenerateDocCommentBody(out, method); out->Print("public virtual $response$ $methodname$($request$ request, CallOptions options)\n", "methodname", method->name(), "request", GetClassName(method->input_type()), "response", @@ -420,6 +483,7 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) { if (method_type == METHODTYPE_NO_STREAMING) { method_name += "Async"; // prevent name clash with synchronous method. } + GenerateDocCommentBody(out, method); out->Print( "public virtual $returntype$ $methodname$($request_maybe$Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))\n", "methodname", method_name, "request_maybe", @@ -435,6 +499,7 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) { out->Print("}\n"); // overload taking CallOptions as a param + GenerateDocCommentBody(out, method); out->Print( "public virtual $returntype$ $methodname$($request_maybe$CallOptions options)\n", "methodname", method_name, "request_maybe", @@ -485,7 +550,7 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) { void GenerateBindServiceMethod(Printer* out, const ServiceDescriptor *service, bool use_server_class) { out->Print( - "// creates service definition that can be registered with a server\n"); + "/// Creates service definition that can be registered with a server\n"); out->Print("#pragma warning disable 0618\n"); out->Print( "public static ServerServiceDefinition BindService($interface$ serviceImpl)\n", @@ -519,7 +584,8 @@ void GenerateBindServiceMethod(Printer* out, const ServiceDescriptor *service, } void GenerateNewStubMethods(Printer* out, const ServiceDescriptor *service) { - out->Print("// creates a new client\n"); + out->Print("/// Creates a new client for $servicename$\n", + "servicename", GetServiceClassName(service)); out->Print("public static $classname$ NewClient(Channel channel)\n", "classname", GetClientClassName(service)); out->Print("{\n"); @@ -534,6 +600,7 @@ void GenerateNewStubMethods(Printer* out, const ServiceDescriptor *service) { void GenerateService(Printer* out, const ServiceDescriptor *service, bool generate_client, bool generate_server, bool internal_access) { + GenerateDocCommentBody(out, service); out->Print("$access_level$ static class $classname$\n", "access_level", GetAccessLevel(internal_access), "classname", GetServiceClassName(service)); @@ -590,6 +657,14 @@ grpc::string GetServices(const FileDescriptor *file, bool generate_client, // Write out a file header. out.Print("// Generated by the protocol buffer compiler. DO NOT EDIT!\n"); out.Print("// source: $filename$\n", "filename", file->name()); + + // use C++ style as there are no file-level XML comments in .NET + grpc::string leading_comments = GetCppComments(file, true); + if (!leading_comments.empty()) { + out.Print("// Original file comments:\n"); + out.Print(leading_comments.c_str()); + } + out.Print("#region Designer generated code\n"); out.Print("\n"); out.Print("using System;\n"); -- cgit v1.2.3