Index: tools/gn/command_format.cc |
diff --git a/tools/gn/command_format.cc b/tools/gn/command_format.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..3e0a27009e4f93e4541ee608cdf27a9ddb1647f4 |
--- /dev/null |
+++ b/tools/gn/command_format.cc |
@@ -0,0 +1,158 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include <sstream> |
+ |
+#include "tools/gn/commands.h" |
+#include "tools/gn/input_file.h" |
+#include "tools/gn/parser.h" |
+#include "tools/gn/scheduler.h" |
+#include "tools/gn/setup.h" |
+#include "tools/gn/source_file.h" |
+#include "tools/gn/tokenizer.h" |
+ |
+namespace commands { |
+ |
+const char kFormat[] = "format"; |
+const char kFormat_HelpShort[] = |
+ "format: Format .gn file."; |
+const char kFormat_Help[] = |
+ "gn format: Format .gn file.\n" |
+ "\n" |
+ " gn format //some/BUILD.gn output.gn\n" |
+ "\n" |
+ " Formats .gn file to a standard format. THIS IS NOT FULLY IMPLEMENTED\n" |
+ " YET! IT WILL EAT BOTH YOUR BEAUTIFUL .GN FILES AND YOUR LAUNDRY.\n"; |
+ |
+namespace { |
+ |
+std::string Indent(int count) { |
+ return std::string(count, ' '); |
+} |
+ |
+void Output(const ParseNode* root, std::ostream& os, int indent); |
+ |
+enum ListStyle { |
+ kListStyleFunctionCall, |
+ kListStyleArray, |
+}; |
+ |
+enum TrailingCommaStyle { |
+ kTrailingCommaNever, |
+ kTrailingCommaNoneIfOneLine, |
+}; |
+ |
+const int kIndentDelta = 2; |
+ |
+void OutputList(const ListNode* list, |
+ std::ostream& os, |
+ int indent, |
+ ListStyle style) { |
+ TrailingCommaStyle trailing_comma = style == kListStyleFunctionCall |
+ ? kTrailingCommaNever |
+ : kTrailingCommaNoneIfOneLine; |
+ const char* open = style == kListStyleFunctionCall ? "(" : "["; |
+ const char* close = style == kListStyleFunctionCall ? ")" : "]"; |
+ os << open; |
+ const size_t size = list->contents().size(); |
+ const bool multiline = size > 1 && style == kListStyleArray; |
+ if (!multiline && style != kListStyleFunctionCall) |
+ os << " "; |
+ for (std::vector<const ParseNode*>::const_iterator i = |
+ list->contents().begin(); |
+ i != list->contents().end(); |
+ ++i) { |
+ if (multiline) |
+ os << "\n" << Indent(indent + kIndentDelta); |
+ Output(*i, os, 0); |
+ if (i + 1 != list->contents().end() || |
+ (multiline && trailing_comma == kTrailingCommaNoneIfOneLine)) |
+ os << ","; |
+ if (!multiline && style != kListStyleFunctionCall) |
+ os << " "; |
+ } |
+ if (multiline) |
+ os << "\n" << Indent(indent); |
+ os << close; |
+} |
+ |
+// TODO(scottmg): All temporary. Needs to do some sort of tracking of current |
+// column and remaining width to 80 chars to handle when to inline and when to |
+// wrap lists, e.g. |
+void Output(const ParseNode* root, std::ostream& os, int indent) { |
+ if (const BlockNode* block = root->AsBlock()) { |
+ for (std::vector<ParseNode*>::const_iterator i(block->statements().begin()); |
+ i != block->statements().end(); |
+ ++i) { |
+ Output(*i, os, indent); |
+ os << "\n"; |
+ if (i + 1 != block->statements().end()) |
+ os << "\n"; |
+ } |
+ } else if (const FunctionCallNode* func_call = root->AsFunctionCall()) { |
+ os << Indent(indent) << func_call->function().value(); |
+ OutputList(func_call->args(), os, indent, kListStyleFunctionCall); |
+ os << " {\n"; |
+ Output(func_call->block(), os, indent + kIndentDelta); |
+ os << Indent(indent) << "}"; |
+ } else if (const ListNode* list = root->AsList()) { |
+ OutputList(list, os, indent, kListStyleArray); |
+ } else if (const BinaryOpNode* binop = root->AsBinaryOp()) { |
+ os << Indent(indent); |
+ Output(binop->left(), os, indent); |
+ os << " " << binop->op().value() << " "; |
+ Output(binop->right(), os, indent); |
+ } else if (const IdentifierNode* identifier = root->AsIdentifier()) { |
+ os << identifier->value().value(); |
+ } else if (const LiteralNode* literal = root->AsLiteral()) { |
+ os << literal->value().value(); |
+ } else { |
+ os << "\nUNKNOWN!\n"; |
+ } |
+} |
+ |
+} // namespace |
+ |
+bool FormatFileToString(const std::string& input_filename, |
+ std::string* output) { |
+ SourceFile input_file(input_filename); |
+ Err err; |
+ Setup setup; |
+ const ParseNode* parse_node = |
+ setup.scheduler().input_file_manager()->SyncLoadFile( |
+ LocationRange(), &setup.build_settings(), input_file, &err); |
+ if (!err.has_error()) { |
+ std::ostringstream collector; |
+ //parse_node->Print(collector, 0); |
+ //collector << "--------------\n"; |
+ Output(parse_node, collector, 0); |
+ *output = collector.str(); |
+ } |
+ return !err.has_error(); |
+} |
+ |
+int RunFormat(const std::vector<std::string>& args) { |
+ // TODO(scottmg): Eventually, this should be a list/spec of files, and they |
+ // should all be done in parallel. For now, we don't want to overwrite good |
+ // data with mistakenly reformatted stuff, so we require an input and an |
+ // output file. |
+ if (args.size() != 2) { |
+ Err(Location(), "Expecting exactly two arguments, see `gn help format`.\n") |
+ .PrintToStdout(); |
+ return 1; |
+ } |
+ |
+ std::string output_string; |
+ |
+ if (FormatFileToString(args[0], &output_string)) { |
+ // TODO(scottmg): Temporary, obviously. |
+ FILE* out = fopen(args[1].c_str(), "w"); |
+ fprintf(out, "%s", output_string.c_str()); |
+ fclose(out); |
+ } |
+ |
+ return 0; |
+} |
+ |
+} // namespace commands |