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 }