OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include <sstream> |
| 6 |
| 7 #include "tools/gn/commands.h" |
| 8 #include "tools/gn/input_file.h" |
| 9 #include "tools/gn/parser.h" |
| 10 #include "tools/gn/scheduler.h" |
| 11 #include "tools/gn/setup.h" |
| 12 #include "tools/gn/source_file.h" |
| 13 #include "tools/gn/tokenizer.h" |
| 14 |
| 15 namespace commands { |
| 16 |
| 17 const char kFormat[] = "format"; |
| 18 const char kFormat_HelpShort[] = |
| 19 "format: Format .gn file."; |
| 20 const char kFormat_Help[] = |
| 21 "gn format: Format .gn file.\n" |
| 22 "\n" |
| 23 " gn format //some/BUILD.gn output.gn\n" |
| 24 "\n" |
| 25 " Formats .gn file to a standard format. THIS IS NOT FULLY IMPLEMENTED\n" |
| 26 " YET! IT WILL EAT BOTH YOUR BEAUTIFUL .GN FILES AND YOUR LAUNDRY.\n"; |
| 27 |
| 28 namespace { |
| 29 |
| 30 std::string Indent(int count) { |
| 31 return std::string(count, ' '); |
| 32 } |
| 33 |
| 34 void Output(const ParseNode* root, std::ostream& os, int indent); |
| 35 |
| 36 enum ListStyle { |
| 37 kListStyleFunctionCall, |
| 38 kListStyleArray, |
| 39 }; |
| 40 |
| 41 enum TrailingCommaStyle { |
| 42 kTrailingCommaNever, |
| 43 kTrailingCommaNoneIfOneLine, |
| 44 }; |
| 45 |
| 46 const int kIndentDelta = 2; |
| 47 |
| 48 void OutputList(const ListNode* list, |
| 49 std::ostream& os, |
| 50 int indent, |
| 51 ListStyle style) { |
| 52 TrailingCommaStyle trailing_comma = style == kListStyleFunctionCall |
| 53 ? kTrailingCommaNever |
| 54 : kTrailingCommaNoneIfOneLine; |
| 55 const char* open = style == kListStyleFunctionCall ? "(" : "["; |
| 56 const char* close = style == kListStyleFunctionCall ? ")" : "]"; |
| 57 os << open; |
| 58 const size_t size = list->contents().size(); |
| 59 const bool multiline = size > 1 && style == kListStyleArray; |
| 60 if (!multiline && style != kListStyleFunctionCall) |
| 61 os << " "; |
| 62 for (std::vector<const ParseNode*>::const_iterator i = |
| 63 list->contents().begin(); |
| 64 i != list->contents().end(); |
| 65 ++i) { |
| 66 if (multiline) |
| 67 os << "\n" << Indent(indent + kIndentDelta); |
| 68 Output(*i, os, 0); |
| 69 if (i + 1 != list->contents().end() || |
| 70 (multiline && trailing_comma == kTrailingCommaNoneIfOneLine)) |
| 71 os << ","; |
| 72 if (!multiline && style != kListStyleFunctionCall) |
| 73 os << " "; |
| 74 } |
| 75 if (multiline) |
| 76 os << "\n" << Indent(indent); |
| 77 os << close; |
| 78 } |
| 79 |
| 80 // TODO(scottmg): All temporary. Needs to do some sort of tracking of current |
| 81 // column and remaining width to 80 chars to handle when to inline and when to |
| 82 // wrap lists, e.g. |
| 83 void Output(const ParseNode* root, std::ostream& os, int indent) { |
| 84 if (const BlockNode* block = root->AsBlock()) { |
| 85 for (std::vector<ParseNode*>::const_iterator i(block->statements().begin()); |
| 86 i != block->statements().end(); |
| 87 ++i) { |
| 88 Output(*i, os, indent); |
| 89 os << "\n"; |
| 90 if (i + 1 != block->statements().end()) |
| 91 os << "\n"; |
| 92 } |
| 93 } else if (const FunctionCallNode* func_call = root->AsFunctionCall()) { |
| 94 os << Indent(indent) << func_call->function().value(); |
| 95 OutputList(func_call->args(), os, indent, kListStyleFunctionCall); |
| 96 os << " {\n"; |
| 97 Output(func_call->block(), os, indent + kIndentDelta); |
| 98 os << Indent(indent) << "}"; |
| 99 } else if (const ListNode* list = root->AsList()) { |
| 100 OutputList(list, os, indent, kListStyleArray); |
| 101 } else if (const BinaryOpNode* binop = root->AsBinaryOp()) { |
| 102 os << Indent(indent); |
| 103 Output(binop->left(), os, indent); |
| 104 os << " " << binop->op().value() << " "; |
| 105 Output(binop->right(), os, indent); |
| 106 } else if (const IdentifierNode* identifier = root->AsIdentifier()) { |
| 107 os << identifier->value().value(); |
| 108 } else if (const LiteralNode* literal = root->AsLiteral()) { |
| 109 os << literal->value().value(); |
| 110 } else { |
| 111 os << "\nUNKNOWN!\n"; |
| 112 } |
| 113 } |
| 114 |
| 115 } // namespace |
| 116 |
| 117 bool FormatFileToString(const std::string& input_filename, |
| 118 std::string* output) { |
| 119 SourceFile input_file(input_filename); |
| 120 Err err; |
| 121 Setup setup; |
| 122 const ParseNode* parse_node = |
| 123 setup.scheduler().input_file_manager()->SyncLoadFile( |
| 124 LocationRange(), &setup.build_settings(), input_file, &err); |
| 125 if (!err.has_error()) { |
| 126 std::ostringstream collector; |
| 127 //parse_node->Print(collector, 0); |
| 128 //collector << "--------------\n"; |
| 129 Output(parse_node, collector, 0); |
| 130 *output = collector.str(); |
| 131 } |
| 132 return !err.has_error(); |
| 133 } |
| 134 |
| 135 int RunFormat(const std::vector<std::string>& args) { |
| 136 // TODO(scottmg): Eventually, this should be a list/spec of files, and they |
| 137 // should all be done in parallel. For now, we don't want to overwrite good |
| 138 // data with mistakenly reformatted stuff, so we require an input and an |
| 139 // output file. |
| 140 if (args.size() != 2) { |
| 141 Err(Location(), "Expecting exactly two arguments, see `gn help format`.\n") |
| 142 .PrintToStdout(); |
| 143 return 1; |
| 144 } |
| 145 |
| 146 std::string output_string; |
| 147 |
| 148 if (FormatFileToString(args[0], &output_string)) { |
| 149 // TODO(scottmg): Temporary, obviously. |
| 150 FILE* out = fopen(args[1].c_str(), "w"); |
| 151 fprintf(out, "%s", output_string.c_str()); |
| 152 fclose(out); |
| 153 } |
| 154 |
| 155 return 0; |
| 156 } |
| 157 |
| 158 } // namespace commands |
OLD | NEW |