kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/cxx/doc/doc.cc (about)

     1  /*
     2   * Copyright 2016 The Kythe Authors. All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *   http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  // doc is a utility that performs simple formatting tasks on documentation
    18  // extracted from the Kythe graph.
    19  
    20  #include <fcntl.h>
    21  #include <unistd.h>
    22  
    23  #include <cstdio>
    24  #include <memory>
    25  #include <string>
    26  
    27  #include "absl/flags/flag.h"
    28  #include "absl/flags/parse.h"
    29  #include "absl/flags/usage.h"
    30  #include "absl/log/check.h"
    31  #include "absl/strings/str_format.h"
    32  #include "google/protobuf/io/zero_copy_stream_impl.h"
    33  #include "google/protobuf/stubs/common.h"
    34  #include "google/protobuf/text_format.h"
    35  #include "kythe/cxx/common/init.h"
    36  #include "kythe/cxx/common/kythe_uri.h"
    37  #include "kythe/cxx/common/net_client.h"
    38  #include "kythe/cxx/common/schema/edges.h"
    39  #include "kythe/cxx/common/schema/facts.h"
    40  #include "kythe/cxx/doc/html_markup_handler.h"
    41  #include "kythe/cxx/doc/html_renderer.h"
    42  #include "kythe/cxx/doc/javadoxygen_markup_handler.h"
    43  #include "kythe/proto/common.pb.h"
    44  #include "kythe/proto/storage.pb.h"
    45  #include "kythe/proto/xref.pb.h"
    46  
    47  ABSL_FLAG(std::string, xrefs, "http://localhost:8080",
    48            "Base URI for xrefs service");
    49  ABSL_FLAG(std::string, corpus, "test", "Default corpus to use");
    50  ABSL_FLAG(std::string, path, "",
    51            "Look up this path in the xrefs service and process all "
    52            "documented nodes inside");
    53  ABSL_FLAG(std::string, save_response, "",
    54            "Save the initial documentation response to this file as an "
    55            "ASCII protobuf.");
    56  ABSL_FLAG(std::string, css, "",
    57            "Include this stylesheet path in the resulting HTML.");
    58  ABSL_FLAG(bool, common_signatures, false,
    59            "Render the MarkedSource proto from standard in.");
    60  
    61  namespace kythe {
    62  namespace {
    63  constexpr char kDocHeaderPrefix[] = R"(<!doctype html>
    64  <html>
    65    <head>
    66      <meta charset="utf-8">
    67  )";
    68  constexpr char kDocHeaderSuffix[] = R"(    <title>Kythe doc output</title>
    69    </head>
    70    <body>
    71  )";
    72  constexpr char kDocFooter[] = R"(
    73    </body>
    74  </html>
    75  )";
    76  
    77  int DocumentNodesFrom(const proto::DocumentationReply& doc_reply) {
    78    ::fputs(kDocHeaderPrefix, stdout);
    79    if (!absl::GetFlag(FLAGS_css).empty()) {
    80      absl::FPrintF(stdout,
    81                    "<link rel=\"stylesheet\" type=\"text/css\" href=\"%s\">",
    82                    absl::GetFlag(FLAGS_css));
    83    }
    84    ::fputs(kDocHeaderSuffix, stdout);
    85    DocumentHtmlRendererOptions options(doc_reply);
    86    options.make_link_uri = [](const proto::Anchor& anchor) {
    87      return anchor.parent();
    88    };
    89    options.kind_name = [&options](const std::string& ticket) {
    90      if (const auto* node = options.node_info(ticket)) {
    91        for (const auto& fact : node->facts()) {
    92          if (fact.first == kythe::common::schema::kFactNodeKind) {
    93            return std::string(fact.second);
    94          }
    95        }
    96      }
    97      return std::string();
    98    };
    99    for (const auto& document : doc_reply.document()) {
   100      if (document.has_text()) {
   101        auto html =
   102            RenderDocument(options, {ParseJavadoxygen, ParseHtml}, document);
   103        ::fputs(html.c_str(), stdout);
   104      }
   105    }
   106    ::fputs(kDocFooter, stdout);
   107    return 0;
   108  }
   109  
   110  int DocumentNodesFromStdin() {
   111    proto::DocumentationReply doc_reply;
   112    google::protobuf::io::FileInputStream file_input_stream(STDIN_FILENO);
   113    CHECK(google::protobuf::TextFormat::Parse(&file_input_stream, &doc_reply));
   114    return DocumentNodesFrom(doc_reply);
   115  }
   116  
   117  int RenderMarkedSourceFromStdin() {
   118    proto::common::MarkedSource marked_source;
   119    google::protobuf::io::FileInputStream file_input_stream(STDIN_FILENO);
   120    CHECK(
   121        google::protobuf::TextFormat::Parse(&file_input_stream, &marked_source));
   122    absl::PrintF("      RenderSimpleIdentifier: \"%s\"\n",
   123                 RenderSimpleIdentifier(marked_source));
   124    auto params = RenderSimpleParams(marked_source);
   125    for (const auto& param : params) {
   126      absl::PrintF("          RenderSimpleParams: \"%s\"\n", param);
   127    }
   128    absl::PrintF("RenderSimpleQualifiedName-ID: \"%s\"\n",
   129                 RenderSimpleQualifiedName(marked_source, false));
   130    absl::PrintF("RenderSimpleQualifiedName+ID: \"%s\"\n",
   131                 RenderSimpleQualifiedName(marked_source, true));
   132    return 0;
   133  }
   134  
   135  int DocumentNodesFrom(XrefsJsonClient* client, const proto::VName& file_name) {
   136    proto::DecorationsRequest request;
   137    proto::DecorationsReply reply;
   138    request.mutable_location()->set_ticket(URI(file_name).ToString());
   139    request.set_references(true);
   140    std::string error;
   141    CHECK(client->Decorations(request, &reply, &error)) << error;
   142    proto::DocumentationRequest doc_request;
   143    proto::DocumentationReply doc_reply;
   144    for (const auto& reference : reply.reference()) {
   145      if (reference.kind() == kythe::common::schema::kDefinesBinding) {
   146        doc_request.add_ticket(reference.target_ticket());
   147      }
   148    }
   149    absl::FPrintF(stderr, "Looking for %d tickets\n", doc_request.ticket_size());
   150    CHECK(client->Documentation(doc_request, &doc_reply, &error)) << error;
   151    if (!absl::GetFlag(FLAGS_save_response).empty()) {
   152      int saved = open(absl::GetFlag(FLAGS_save_response).c_str(),
   153                       O_CREAT | O_TRUNC | O_WRONLY, 0640);
   154      if (saved < 0) {
   155        absl::FPrintF(stderr, "Couldn't open %s\n",
   156                      absl::GetFlag(FLAGS_save_response));
   157        return 1;
   158      }
   159      {
   160        google::protobuf::io::FileOutputStream outfile(saved);
   161        if (!google::protobuf::TextFormat::Print(doc_reply, &outfile)) {
   162          absl::FPrintF(stderr, "Coudln't print to %s\n",
   163                        absl::GetFlag(FLAGS_save_response).c_str());
   164          close(saved);
   165          return 1;
   166        }
   167      }
   168      if (close(saved) < 0) {
   169        absl::FPrintF(stderr, "Couldn't close %s\n",
   170                      absl::GetFlag(FLAGS_save_response));
   171        return 1;
   172      }
   173    }
   174    return DocumentNodesFrom(doc_reply);
   175  }
   176  }  // anonymous namespace
   177  }  // namespace kythe
   178  
   179  int main(int argc, char** argv) {
   180    GOOGLE_PROTOBUF_VERIFY_VERSION;
   181    kythe::InitializeProgram(argv[0]);
   182    absl::SetProgramUsageMessage(R"(perform simple documentation formatting
   183  
   184  doc -corpus foo -file bar.cc
   185    Formats documentation for all nodes attached via defines/binding anchors to
   186    a file with path bar.cc in corpus foo.
   187  doc
   188    Formats documentation from a text-format proto::DocumentationReply provided
   189    on standard input.
   190  doc -common_signatures
   191    Renders the text-format proto::common::MarkedSource message provided on standard
   192    input into several common forms.
   193  )");
   194    absl::ParseCommandLine(argc, argv);
   195    if (absl::GetFlag(FLAGS_common_signatures)) {
   196      return kythe::RenderMarkedSourceFromStdin();
   197    } else if (absl::GetFlag(FLAGS_path).empty()) {
   198      return kythe::DocumentNodesFromStdin();
   199    } else {
   200      kythe::JsonClient::InitNetwork();
   201      kythe::XrefsJsonClient client(std::make_unique<kythe::JsonClient>(),
   202                                    absl::GetFlag(FLAGS_xrefs));
   203      auto ticket = kythe::URI::FromString(absl::GetFlag(FLAGS_path));
   204      if (!ticket.first) {
   205        ticket = kythe::URI::FromString(
   206            "kythe://" +
   207            kythe::UriEscape(kythe::UriEscapeMode::kEscapePaths,
   208                             absl::GetFlag(FLAGS_corpus)) +
   209            "?path=" +
   210            kythe::UriEscape(kythe::UriEscapeMode::kEscapePaths,
   211                             absl::GetFlag(FLAGS_path)));
   212      }
   213      if (!ticket.first) {
   214        absl::FPrintF(stderr, "Couldn't parse URI %s\n",
   215                      absl::GetFlag(FLAGS_path));
   216        return 1;
   217      }
   218      return kythe::DocumentNodesFrom(&client, ticket.second.v_name());
   219    }
   220    return 0;
   221  }