#include <sstream>
#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/strings/string_split.h"
#include "tools/gn/commands.h"
+#include "tools/gn/filesystem_utils.h"
#include "tools/gn/input_file.h"
#include "tools/gn/parser.h"
#include "tools/gn/scheduler.h"
namespace commands {
const char kSwitchDumpTree[] = "dump-tree";
+const char kSwitchInPlace[] = "in-place";
+const char kSwitchStdin[] = "stdin";
const char kFormat[] = "format";
const char kFormat_HelpShort[] =
- "format: Format .gn file.";
+ "format: Format .gn file. (ALPHA, WILL DESTROY DATA!)";
const char kFormat_Help[] =
- "gn format: Format .gn file. (ALPHA, WILL CURRENTLY DESTROY DATA!)\n"
- "\n"
- " gn format //some/BUILD.gn\n"
- " gn format some\\BUILD.gn\n"
+ "gn format [--dump-tree] [--in-place] [--stdin] BUILD.gn\n"
"\n"
" Formats .gn file to a standard format. THIS IS NOT FULLY IMPLEMENTED\n"
" YET! IT WILL EAT YOUR BEAUTIFUL .GN FILES. AND YOUR LAUNDRY.\n"
" At a minimum, make sure everything is `git commit`d so you can\n"
- " `git checkout -f` to recover.\n";
+ " `git checkout -f` to recover.\n"
+ "\n"
+ "Arguments\n"
+ " --dump-tree\n"
+ " For debugging only, dumps the parse tree.\n"
+ "\n"
+ " --in-place\n"
+ " Instead writing the formatted file to stdout, replace the input\n"
+ " with the formatted output.\n"
+ "\n"
+ " --stdin\n"
+ " Read input from stdin (and write to stdout). Not compatible with\n"
+ " --in-place of course.\n"
+ "\n"
+ "Examples\n"
+ " gn format //some/BUILD.gn\n"
+ " gn format some\\BUILD.gn\n"
+ " gn format /abspath/some/BUILD.gn\n"
+ " gn format --stdin\n";
namespace {
const int kIndentSize = 2;
+const int kMaximumWidth = 80;
+
+enum Precedence {
+ kPrecedenceLowest,
+ kPrecedenceAssign,
+ kPrecedenceOr,
+ kPrecedenceAnd,
+ kPrecedenceCompare,
+ kPrecedenceAdd,
+ kPrecedenceSuffix,
+ kPrecedenceUnary,
+};
class Printer {
public:
private:
// Format a list of values using the given style.
enum SequenceStyle {
- kSequenceStyleFunctionCall,
kSequenceStyleList,
kSequenceStyleBlock,
+ kSequenceStyleBracedBlock,
};
enum ExprStyle {
kExprStyleComment,
};
+ struct Metrics {
+ Metrics() : first_length(-1), longest_length(-1), multiline(false) {}
+ int first_length;
+ int longest_length;
+ bool multiline;
+ };
+
// Add to output.
void Print(base::StringPiece str);
// Remove trailing spaces from the current line.
void Trim();
+ // Whether there's a blank separator line at the current position.
+ bool HaveBlankLine();
+
+ bool IsAssignment(const ParseNode* node);
+
+ // Heuristics to decide if there should be a blank line added between two
+ // items. For various "small" items, it doesn't look nice if there's too much
+ // vertical whitespace added.
+ bool ShouldAddBlankLineInBetween(const ParseNode* a, const ParseNode* b);
+
// Get the 0-based x position on the current line.
int CurrentColumn();
+ // Adds an opening ( if prec is less than the outers (to maintain evalution
+ // order for a subexpression). If an opening paren is emitted, *parenthesized
+ // will be set so it can be closed at the end of the expression.
+ void AddParen(int prec, int outer_prec, bool* parenthesized);
+
// Print the expression to the output buffer. Returns the type of element
- // added to the output.
- ExprStyle Expr(const ParseNode* root);
+ // added to the output. The value of outer_prec gives the precedence of the
+ // operator outside this Expr. If that operator binds tighter than root's,
+ // Expr must introduce parentheses.
+ ExprStyle Expr(const ParseNode* root, int outer_prec);
+
+ // Use a sub-Printer recursively to figure out the size that an expression
+ // would be before actually adding it to the output.
+ Metrics GetLengthOfExpr(const ParseNode* expr, int outer_prec);
+ // Format a list of values using the given style.
+ // |end| holds any trailing comments to be printed just before the closing
+ // bracket.
template <class PARSENODE> // Just for const covariance.
- void Sequence(SequenceStyle style, const std::vector<PARSENODE*>& list);
+ void Sequence(SequenceStyle style,
+ const std::vector<PARSENODE*>& list,
+ const ParseNode* end);
+
+ void FunctionCall(const FunctionCallNode* func_call);
std::string output_; // Output buffer.
std::vector<Token> comments_; // Pending end-of-line comments.
int margin_; // Left margin (number of spaces).
+ // Gives the precedence for operators in a BinaryOpNode.
+ std::map<base::StringPiece, Precedence> precedence_;
+
DISALLOW_COPY_AND_ASSIGN(Printer);
};
Printer::Printer() : margin_(0) {
output_.reserve(100 << 10);
+ precedence_["="] = kPrecedenceAssign;
+ precedence_["+="] = kPrecedenceAssign;
+ precedence_["-="] = kPrecedenceAssign;
+ precedence_["||"] = kPrecedenceOr;
+ precedence_["&&"] = kPrecedenceAnd;
+ precedence_["<"] = kPrecedenceCompare;
+ precedence_[">"] = kPrecedenceCompare;
+ precedence_["=="] = kPrecedenceCompare;
+ precedence_["!="] = kPrecedenceCompare;
+ precedence_["<="] = kPrecedenceCompare;
+ precedence_[">="] = kPrecedenceCompare;
+ precedence_["+"] = kPrecedenceAdd;
+ precedence_["-"] = kPrecedenceAdd;
+ precedence_["!"] = kPrecedenceUnary;
}
Printer::~Printer() {
if (!comments_.empty()) {
Print(" ");
int i = 0;
+ // Save the margin, and temporarily set it to where the first comment
+ // starts so that multiple suffix comments are vertically aligned. This
+ // will need to be fancier once we enforce 80 col.
+ int old_margin = margin_;
for (const auto& c : comments_) {
- if (i > 0) {
+ if (i == 0)
+ margin_ = CurrentColumn();
+ else {
Trim();
Print("\n");
PrintMargin();
}
TrimAndPrintToken(c);
+ ++i;
}
+ margin_ = old_margin;
comments_.clear();
}
Trim();
output_.resize(n);
}
+bool Printer::HaveBlankLine() {
+ size_t n = output_.size();
+ while (n > 0 && output_[n - 1] == ' ')
+ --n;
+ return n > 2 && output_[n - 1] == '\n' && output_[n - 2] == '\n';
+}
+
+bool Printer::IsAssignment(const ParseNode* node) {
+ return node->AsBinaryOp() && (node->AsBinaryOp()->op().value() == "=" ||
+ node->AsBinaryOp()->op().value() == "+=" ||
+ node->AsBinaryOp()->op().value() == "-=");
+}
+
+bool Printer::ShouldAddBlankLineInBetween(const ParseNode* a,
+ const ParseNode* b) {
+ LocationRange a_range = a->GetRange();
+ LocationRange b_range = b->GetRange();
+ // If they're already separated by 1 or more lines, then we want to keep a
+ // blank line.
+ return b_range.begin().line_number() > a_range.end().line_number() + 1;
+}
+
int Printer::CurrentColumn() {
int n = 0;
while (n < static_cast<int>(output_.size()) &&
size_t i = 0;
for (const auto& stmt : block->statements()) {
- Expr(stmt);
+ Expr(stmt, kPrecedenceLowest);
Newline();
if (stmt->comments()) {
// Why are before() not printed here too? before() are handled inside
Newline();
}
}
- if (i < block->statements().size() - 1)
+ if (i < block->statements().size() - 1 &&
+ (ShouldAddBlankLineInBetween(block->statements()[i],
+ block->statements()[i + 1]))) {
Newline();
+ }
++i;
}
}
}
-Printer::ExprStyle Printer::Expr(const ParseNode* root) {
+Printer::Metrics Printer::GetLengthOfExpr(const ParseNode* expr,
+ int outer_prec) {
+ Metrics result;
+ Printer sub;
+ sub.Expr(expr, outer_prec);
+ std::vector<std::string> lines;
+ base::SplitStringDontTrim(sub.String(), '\n', &lines);
+ result.multiline = lines.size() > 1;
+ result.first_length = static_cast<int>(lines[0].size());
+ for (const auto& line : lines) {
+ result.longest_length =
+ std::max(result.longest_length, static_cast<int>(line.size()));
+ }
+ return result;
+}
+
+void Printer::AddParen(int prec, int outer_prec, bool* parenthesized) {
+ if (prec < outer_prec) {
+ Print("(");
+ *parenthesized = true;
+ }
+}
+
+Printer::ExprStyle Printer::Expr(const ParseNode* root, int outer_prec) {
ExprStyle result = kExprStyleRegular;
if (root->comments()) {
if (!root->comments()->before().empty()) {
}
}
- if (root->AsAccessor()) {
- Print("TODO(scottmg): AccessorNode");
+ bool parenthesized = false;
+
+ if (const AccessorNode* accessor = root->AsAccessor()) {
+ AddParen(kPrecedenceSuffix, outer_prec, &parenthesized);
+ Print(accessor->base().value());
+ if (accessor->member()) {
+ Print(".");
+ Expr(accessor->member(), kPrecedenceLowest);
+ } else {
+ CHECK(accessor->index());
+ Print("[");
+ Expr(accessor->index(), kPrecedenceLowest);
+ Print("]");
+ }
} else if (const BinaryOpNode* binop = root->AsBinaryOp()) {
- // TODO(scottmg): Lots to do here for complex if expressions: reflowing,
- // parenthesizing, etc.
- Expr(binop->left());
- Print(" ");
- Print(binop->op().value());
- Print(" ");
- Expr(binop->right());
+ CHECK(precedence_.find(binop->op().value()) != precedence_.end());
+ Precedence prec = precedence_[binop->op().value()];
+ AddParen(prec, outer_prec, &parenthesized);
+ Metrics right = GetLengthOfExpr(binop->right(), prec + 1);
+ int op_length = static_cast<int>(binop->op().value().size()) + 2;
+ Expr(binop->left(), prec);
+ if (CurrentColumn() + op_length + right.first_length <= kMaximumWidth) {
+ // If it just fits normally, put it here.
+ Print(" ");
+ Print(binop->op().value());
+ Print(" ");
+ Expr(binop->right(), prec + 1);
+ } else {
+ // Otherwise, put first argument and op, and indent next.
+ Print(" ");
+ Print(binop->op().value());
+ int old_margin = margin_;
+ margin_ += kIndentSize * 2;
+ Newline();
+ Expr(binop->right(), prec + 1);
+ margin_ = old_margin;
+ }
} else if (const BlockNode* block = root->AsBlock()) {
- Sequence(kSequenceStyleBlock, block->statements());
+ Sequence(kSequenceStyleBracedBlock, block->statements(), block->End());
} else if (const ConditionNode* condition = root->AsConditionNode()) {
Print("if (");
- Expr(condition->condition());
- Print(") {");
- margin_ += kIndentSize;
- Newline();
- Block(condition->if_true());
- margin_ -= kIndentSize;
- Trim();
- PrintMargin();
- Print("}");
+ Expr(condition->condition(), kPrecedenceLowest);
+ Print(") ");
+ Sequence(kSequenceStyleBracedBlock,
+ condition->if_true()->statements(),
+ condition->if_true()->End());
if (condition->if_false()) {
Print(" else ");
// If it's a block it's a bare 'else', otherwise it's an 'else if'. See
// ConditionNode::Execute.
bool is_else_if = condition->if_false()->AsBlock() == NULL;
if (is_else_if) {
- Expr(condition->if_false());
+ Expr(condition->if_false(), kPrecedenceLowest);
} else {
- Print("{");
- margin_ += kIndentSize;
- Newline();
- Block(condition->if_false());
- margin_ -= kIndentSize;
- Trim();
- PrintMargin();
- Print("}");
+ Sequence(kSequenceStyleBracedBlock,
+ condition->if_false()->AsBlock()->statements(),
+ condition->if_false()->AsBlock()->End());
}
}
} else if (const FunctionCallNode* func_call = root->AsFunctionCall()) {
- Print(func_call->function().value());
- Sequence(kSequenceStyleFunctionCall, func_call->args()->contents());
- Print(" {");
- margin_ += kIndentSize;
- Newline();
- Block(func_call->block());
- margin_ -= kIndentSize;
- Trim();
- PrintMargin();
- Print("}");
+ FunctionCall(func_call);
} else if (const IdentifierNode* identifier = root->AsIdentifier()) {
Print(identifier->value().value());
} else if (const ListNode* list = root->AsList()) {
- Sequence(kSequenceStyleList, list->contents());
+ Sequence(kSequenceStyleList, list->contents(), list->End());
} else if (const LiteralNode* literal = root->AsLiteral()) {
// TODO(scottmg): Quoting?
Print(literal->value().value());
} else if (const UnaryOpNode* unaryop = root->AsUnaryOp()) {
Print(unaryop->op().value());
- Expr(unaryop->operand());
+ Expr(unaryop->operand(), kPrecedenceUnary);
} else if (const BlockCommentNode* block_comment = root->AsBlockComment()) {
Print(block_comment->comment().value());
result = kExprStyleComment;
+ } else if (const EndNode* end = root->AsEnd()) {
+ Print(end->value().value());
} else {
CHECK(false) << "Unhandled case in Expr.";
}
+ if (parenthesized)
+ Print(")");
+
// Defer any end of line comment until we reach the newline.
if (root->comments() && !root->comments()->suffix().empty()) {
std::copy(root->comments()->suffix().begin(),
template <class PARSENODE>
void Printer::Sequence(SequenceStyle style,
- const std::vector<PARSENODE*>& list) {
+ const std::vector<PARSENODE*>& list,
+ const ParseNode* end) {
bool force_multiline = false;
- if (style == kSequenceStyleFunctionCall)
- Print("(");
- else if (style == kSequenceStyleList)
+ if (style == kSequenceStyleList)
Print("[");
+ else if (style == kSequenceStyleBracedBlock)
+ Print("{");
- if (style == kSequenceStyleBlock)
+ if (style == kSequenceStyleBlock || style == kSequenceStyleBracedBlock)
+ force_multiline = true;
+
+ if (end && end->comments() && !end->comments()->before().empty())
force_multiline = true;
// If there's before line comments, make sure we have a place to put them.
if (list.size() == 0 && !force_multiline) {
// No elements, and not forcing newlines, print nothing.
} else if (list.size() == 1 && !force_multiline) {
- if (style != kSequenceStyleFunctionCall)
- Print(" ");
- Expr(list[0]);
- CHECK(list[0]->comments()->after().empty());
- if (style != kSequenceStyleFunctionCall)
- Print(" ");
+ Print(" ");
+ Expr(list[0], kPrecedenceLowest);
+ CHECK(!list[0]->comments() || list[0]->comments()->after().empty());
+ Print(" ");
} else {
margin_ += kIndentSize;
size_t i = 0;
for (const auto& x : list) {
Newline();
- ExprStyle expr_style = Expr(x);
- CHECK(x->comments()->after().empty());
+ // If:
+ // - we're going to output some comments, and;
+ // - we haven't just started this multiline list, and;
+ // - there isn't already a blank line here;
+ // Then: insert one.
+ if (i != 0 && x->comments() && !x->comments()->before().empty() &&
+ !HaveBlankLine()) {
+ Newline();
+ }
+ ExprStyle expr_style = Expr(x, kPrecedenceLowest);
+ CHECK(!x->comments() || x->comments()->after().empty());
if (i < list.size() - 1 || style == kSequenceStyleList) {
- if (expr_style == kExprStyleRegular)
+ if (style == kSequenceStyleList && expr_style == kExprStyleRegular) {
Print(",");
- else
- Newline();
+ } else {
+ if (i < list.size() - 1 &&
+ ShouldAddBlankLineInBetween(list[i], list[i + 1]))
+ Newline();
+ }
}
++i;
}
+ // Trailing comments.
+ if (end->comments()) {
+ if (list.size() >= 2)
+ Newline();
+ for (const auto& c : end->comments()->before()) {
+ Newline();
+ TrimAndPrintToken(c);
+ }
+ }
+
margin_ -= kIndentSize;
Newline();
+
+ // Defer any end of line comment until we reach the newline.
+ if (end->comments() && !end->comments()->suffix().empty()) {
+ std::copy(end->comments()->suffix().begin(),
+ end->comments()->suffix().end(),
+ std::back_inserter(comments_));
+ }
}
- if (style == kSequenceStyleFunctionCall)
- Print(")");
- else if (style == kSequenceStyleList)
+ if (style == kSequenceStyleList)
Print("]");
+ else if (style == kSequenceStyleBracedBlock)
+ Print("}");
}
-} // namespace
+void Printer::FunctionCall(const FunctionCallNode* func_call) {
+ Print(func_call->function().value());
+ Print("(");
-bool FormatFileToString(const std::string& input_filename,
- bool dump_tree,
- std::string* output) {
- Setup setup;
- Err err;
- SourceFile input_file(input_filename);
- const ParseNode* parse_node =
- setup.scheduler().input_file_manager()->SyncLoadFile(
- LocationRange(), &setup.build_settings(), input_file, &err);
- if (err.has_error()) {
- err.PrintToStdout();
- return false;
+ int old_margin = margin_;
+ bool have_block = func_call->block() != nullptr;
+ bool force_multiline = false;
+
+ const std::vector<const ParseNode*>& list = func_call->args()->contents();
+ const ParseNode* end = func_call->args()->End();
+
+ if (end && end->comments() && !end->comments()->before().empty())
+ force_multiline = true;
+
+ // If there's before line comments, make sure we have a place to put them.
+ for (const auto& i : list) {
+ if (i->comments() && !i->comments()->before().empty())
+ force_multiline = true;
+ }
+
+ // Calculate the length of the items for function calls so we can decide to
+ // compress them in various nicer ways.
+ std::vector<int> natural_lengths;
+ bool fits_on_current_line = true;
+ int max_item_width = 0;
+ int total_length = 0;
+ natural_lengths.reserve(list.size());
+ std::string terminator = ")";
+ if (have_block)
+ terminator += " {";
+ for (size_t i = 0; i < list.size(); ++i) {
+ Metrics sub = GetLengthOfExpr(list[i], kPrecedenceLowest);
+ if (sub.multiline)
+ fits_on_current_line = false;
+ natural_lengths.push_back(sub.longest_length);
+ total_length += sub.longest_length;
+ if (i < list.size() - 1) {
+ total_length += static_cast<int>(strlen(", "));
+ }
+ }
+ fits_on_current_line =
+ fits_on_current_line &&
+ CurrentColumn() + total_length + terminator.size() <= kMaximumWidth;
+ if (natural_lengths.size() > 0) {
+ max_item_width =
+ *std::max_element(natural_lengths.begin(), natural_lengths.end());
}
+
+ if (list.size() == 0 && !force_multiline) {
+ // No elements, and not forcing newlines, print nothing.
+ } else if (list.size() == 1 && !force_multiline && fits_on_current_line) {
+ Expr(list[0], kPrecedenceLowest);
+ CHECK(!list[0]->comments() || list[0]->comments()->after().empty());
+ } else {
+ // Function calls get to be single line even with multiple arguments, if
+ // they fit inside the maximum width.
+ if (!force_multiline && fits_on_current_line) {
+ for (size_t i = 0; i < list.size(); ++i) {
+ Expr(list[i], kPrecedenceLowest);
+ if (i < list.size() - 1)
+ Print(", ");
+ }
+ } else {
+ bool should_break_to_next_line = true;
+ int indent = kIndentSize * 2;
+ if (CurrentColumn() + max_item_width + terminator.size() <=
+ kMaximumWidth ||
+ CurrentColumn() < margin_ + indent) {
+ should_break_to_next_line = false;
+ margin_ = CurrentColumn();
+ } else {
+ margin_ += indent;
+ }
+ size_t i = 0;
+ for (const auto& x : list) {
+ // Function calls where all the arguments would fit at the current
+ // position should do that instead of going back to margin+4.
+ if (i > 0 || should_break_to_next_line)
+ Newline();
+ ExprStyle expr_style = Expr(x, kPrecedenceLowest);
+ CHECK(!x->comments() || x->comments()->after().empty());
+ if (i < list.size() - 1) {
+ if (expr_style == kExprStyleRegular) {
+ Print(",");
+ } else {
+ Newline();
+ }
+ }
+ ++i;
+ }
+
+ // Trailing comments.
+ if (end->comments()) {
+ if (!list.empty())
+ Newline();
+ for (const auto& c : end->comments()->before()) {
+ Newline();
+ TrimAndPrintToken(c);
+ }
+ if (!end->comments()->before().empty())
+ Newline();
+ }
+ }
+ }
+
+ // Defer any end of line comment until we reach the newline.
+ if (end->comments() && !end->comments()->suffix().empty()) {
+ std::copy(end->comments()->suffix().begin(),
+ end->comments()->suffix().end(),
+ std::back_inserter(comments_));
+ }
+
+ Print(")");
+ margin_ = old_margin;
+
+ if (have_block) {
+ Print(" ");
+ Sequence(kSequenceStyleBracedBlock,
+ func_call->block()->statements(),
+ func_call->block()->End());
+ }
+}
+
+void DoFormat(const ParseNode* root, bool dump_tree, std::string* output) {
if (dump_tree) {
std::ostringstream os;
- parse_node->Print(os, 0);
+ root->Print(os, 0);
printf("----------------------\n");
printf("-- PARSE TREE --------\n");
printf("----------------------\n");
printf("----------------------\n");
}
Printer pr;
- pr.Block(parse_node);
+ pr.Block(root);
*output = pr.String();
+}
+
+std::string ReadStdin() {
+ static const int kBufferSize = 256;
+ char buffer[kBufferSize];
+ std::string result;
+ while (true) {
+ char* input = NULL;
+ input = fgets(buffer, kBufferSize, stdin);
+ if (input == NULL && feof(stdin))
+ return result;
+ int length = static_cast<int>(strlen(buffer));
+ if (length == 0)
+ return result;
+ else
+ result += std::string(buffer, length);
+ }
+}
+
+} // namespace
+
+bool FormatFileToString(Setup* setup,
+ const SourceFile& file,
+ bool dump_tree,
+ std::string* output) {
+ Err err;
+ const ParseNode* parse_node =
+ setup->scheduler().input_file_manager()->SyncLoadFile(
+ LocationRange(), &setup->build_settings(), file, &err);
+ if (err.has_error()) {
+ err.PrintToStdout();
+ return false;
+ }
+ DoFormat(parse_node, dump_tree, output);
+ return true;
+}
+
+bool FormatStringToString(const std::string& input,
+ bool dump_tree,
+ std::string* output) {
+ SourceFile source_file;
+ InputFile file(source_file);
+ file.SetContents(input);
+ Err err;
+ // Tokenize.
+ std::vector<Token> tokens = Tokenizer::Tokenize(&file, &err);
+ if (err.has_error()) {
+ err.PrintToStdout();
+ return false;
+ }
+
+ // Parse.
+ scoped_ptr<ParseNode> parse_node = Parser::Parse(tokens, &err);
+ if (err.has_error()) {
+ err.PrintToStdout();
+ return false;
+ }
+
+ DoFormat(parse_node.get(), dump_tree, output);
return true;
}
int RunFormat(const std::vector<std::string>& args) {
+ bool dump_tree =
+ base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDumpTree);
+
+ bool from_stdin =
+ base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchStdin);
+
+ if (from_stdin) {
+ if (args.size() != 0) {
+ Err(Location(), "Expecting no arguments when reading from stdin.\n")
+ .PrintToStdout();
+ return 1;
+ }
+ std::string input = ReadStdin();
+ std::string output;
+ if (!FormatStringToString(input, dump_tree, &output))
+ return 1;
+ printf("%s", output.c_str());
+ return 0;
+ }
+
// TODO(scottmg): Eventually, this should be a list/spec of files, and they
- // should all be done in parallel and in-place. For now, we don't want to
- // overwrite good data with mistakenly reformatted stuff, so we just simply
- // print the formatted output to stdout.
+ // should all be done in parallel.
if (args.size() != 1) {
Err(Location(), "Expecting exactly one argument, see `gn help format`.\n")
.PrintToStdout();
return 1;
}
- bool dump_tree =
- base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDumpTree);
+ Setup setup;
+ SourceDir source_dir =
+ SourceDirForCurrentDirectory(setup.build_settings().root_path());
+ SourceFile file = source_dir.ResolveRelativeFile(args[0]);
- std::string input_name = args[0];
- if (input_name[0] != '/') {
- std::replace(input_name.begin(), input_name.end(), '\\', '/');
- input_name = "//" + input_name;
- }
std::string output_string;
- if (FormatFileToString(input_name, dump_tree, &output_string)) {
- printf("%s", output_string.c_str());
+ if (FormatFileToString(&setup, file, dump_tree, &output_string)) {
+ bool in_place =
+ base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchInPlace);
+ if (in_place) {
+ base::FilePath to_write = setup.build_settings().GetFullPath(file);
+ if (base::WriteFile(to_write,
+ output_string.data(),
+ static_cast<int>(output_string.size())) == -1) {
+ Err(Location(),
+ std::string("Failed to write formatted output back to \"") +
+ to_write.AsUTF8Unsafe() + std::string("\".")).PrintToStdout();
+ return 1;
+ }
+ printf("Wrote formatted to '%s'.\n", to_write.AsUTF8Unsafe().c_str());
+ } else {
+ printf("%s", output_string.c_str());
+ }
}
return 0;