Add --dump-json-tree option for format subcommand.
The option allows to output a token tree in the JSON format, which is
intended to be used by another program.
This feature will be firstly used for making a linter of .gn files
(crbug.com/912393).
Change-Id: I3b97cb6fbd3fb672c38ae57b63db8f1b9f983146
Reviewed-on: https://gn-review.googlesource.com/c/3860
Commit-Queue: Brett Wilson <brettw@google.com>
Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/tools/gn/command_format.cc b/tools/gn/command_format.cc
index b620013..5b9580b 100644
--- a/tools/gn/command_format.cc
+++ b/tools/gn/command_format.cc
@@ -10,6 +10,7 @@
#include "base/command_line.h"
#include "base/files/file_util.h"
+#include "base/json/json_writer.h"
#include "base/macros.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
@@ -26,6 +27,8 @@
const char kSwitchDryRun[] = "dry-run";
const char kSwitchDumpTree[] = "dump-tree";
+const char kSwitchDumpTreeText[] = "text";
+const char kSwitchDumpTreeJSON[] = "json";
const char kSwitchStdin[] = "stdin";
const char kFormat[] = "format";
@@ -55,9 +58,9 @@
- Exit code 1: general failure (parse error, etc.)
- Exit code 2: successful format, but differs from on disk.
- --dump-tree
- For debugging, dumps the parse tree to stdout and does not update the
- file or print formatted output.
+ --dump-tree[=( text | json )]
+ Dumps the parse tree to stdout and does not update the file or print
+ formatted output. If no format is specified, text format will be used.
--stdin
Read input from stdin and write to stdout rather than update a file
@@ -1059,12 +1062,19 @@
return false;
}
-void DoFormat(const ParseNode* root, bool dump_tree, std::string* output) {
- if (dump_tree) {
+void DoFormat(const ParseNode* root, TreeDumpMode dump_tree,
+ std::string* output) {
+ if (dump_tree == TreeDumpMode::kPlainText) {
std::ostringstream os;
- root->Print(os, 0);
+ RenderToText(root->GetJSONNode(), 0, os);
fprintf(stderr, "%s", os.str().c_str());
+ } else if (dump_tree == TreeDumpMode::kJSON) {
+ std::string os;
+ base::JSONWriter::WriteWithOptions(root->GetJSONNode(),
+ base::JSONWriter::OPTIONS_PRETTY_PRINT, &os);
+ fprintf(stderr, "%s", os.c_str());
}
+
Printer pr;
pr.Block(root);
*output = pr.String();
@@ -1091,7 +1101,7 @@
bool FormatFileToString(Setup* setup,
const SourceFile& file,
- bool dump_tree,
+ TreeDumpMode dump_tree,
std::string* output) {
Err err;
const ParseNode* parse_node =
@@ -1106,7 +1116,7 @@
}
bool FormatStringToString(const std::string& input,
- bool dump_tree,
+ TreeDumpMode dump_tree,
std::string* output) {
SourceFile source_file;
InputFile file(source_file);
@@ -1133,8 +1143,23 @@
int RunFormat(const std::vector<std::string>& args) {
bool dry_run =
base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDryRun);
- bool dump_tree =
- base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDumpTree);
+ TreeDumpMode dump_tree = TreeDumpMode::kInactive;
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDumpTree)) {
+ std::string tree_type = base::CommandLine::ForCurrentProcess()->
+ GetSwitchValueASCII(kSwitchDumpTree);
+ if (tree_type == kSwitchDumpTreeJSON) {
+ dump_tree = TreeDumpMode::kJSON;
+ } else if (tree_type.empty() || tree_type == kSwitchDumpTreeText) {
+ dump_tree = TreeDumpMode::kPlainText;
+ } else {
+ Err(Location(),
+ tree_type + " is an invalid value for --dump-tree. Specify "
+ "\"" + kSwitchDumpTreeText + "\" or \"" + kSwitchDumpTreeJSON +
+ "\".\n")
+ .PrintToStdout();
+ return 1;
+ }
+ }
bool from_stdin =
base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchStdin);
@@ -1180,7 +1205,7 @@
std::string output_string;
if (FormatFileToString(&setup, file, dump_tree, &output_string)) {
- if (!dump_tree) {
+ if (dump_tree == TreeDumpMode::kInactive) {
// Update the file in-place.
base::FilePath to_write = setup.build_settings().GetFullPath(file);
std::string original_contents;
diff --git a/tools/gn/command_format.h b/tools/gn/command_format.h
index 413937e..785b070 100644
--- a/tools/gn/command_format.h
+++ b/tools/gn/command_format.h
@@ -12,13 +12,25 @@
namespace commands {
+enum class TreeDumpMode {
+ // Normal operation mode. Format the input file.
+ kInactive,
+
+ // Output the token tree with indented plain text. For debugging.
+ kPlainText,
+
+ // Output the token tree in JSON format. Used for exporting a tree to another
+ // program.
+ kJSON
+};
+
bool FormatFileToString(Setup* setup,
const SourceFile& file,
- bool dump_tree,
+ TreeDumpMode dump_tree,
std::string* output);
bool FormatStringToString(const std::string& input,
- bool dump_tree,
+ TreeDumpMode dump_tree,
std::string* output);
} // namespace commands
diff --git a/tools/gn/command_format_unittest.cc b/tools/gn/command_format_unittest.cc
index bad12ee..b00f6ac 100644
--- a/tools/gn/command_format_unittest.cc
+++ b/tools/gn/command_format_unittest.cc
@@ -23,8 +23,8 @@
GetExePath().DirName().Append(FILE_PATH_LITERAL("..")); \
base::SetCurrentDirectory(src_dir); \
EXPECT_TRUE(commands::FormatFileToString( \
- &setup, SourceFile("//tools/gn/format_test_data/" #n ".gn"), false, \
- &out)); \
+ &setup, SourceFile("//tools/gn/format_test_data/" #n ".gn"), \
+ commands::TreeDumpMode::kInactive, &out)); \
ASSERT_TRUE(base::ReadFileToString( \
base::FilePath(FILE_PATH_LITERAL("tools/gn/format_test_data/") \
FILE_PATH_LITERAL(#n) \
@@ -33,7 +33,8 @@
EXPECT_EQ(expected, out); \
/* Make sure formatting the output doesn't cause further changes. */ \
std::string out_again; \
- EXPECT_TRUE(commands::FormatStringToString(out, false, &out_again)); \
+ EXPECT_TRUE(commands::FormatStringToString(out, \
+ commands::TreeDumpMode::kInactive, &out_again)); \
ASSERT_EQ(out, out_again); \
}
diff --git a/tools/gn/operators_unittest.cc b/tools/gn/operators_unittest.cc
index db90ac9..2569bc5 100644
--- a/tools/gn/operators_unittest.cc
+++ b/tools/gn/operators_unittest.cc
@@ -40,7 +40,9 @@
const std::string& help) const override {
return Err(this, msg);
}
- void Print(std::ostream& out, int indent) const override {}
+ base::Value GetJSONNode() const override {
+ return base::Value();
+ }
private:
Value value_;
diff --git a/tools/gn/parse_tree.cc b/tools/gn/parse_tree.cc
index 6dde01d..821fb7e 100644
--- a/tools/gn/parse_tree.cc
+++ b/tools/gn/parse_tree.cc
@@ -10,6 +10,7 @@
#include <string>
#include <tuple>
+#include "base/json/string_escape.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "tools/gn/functions.h"
@@ -17,6 +18,14 @@
#include "tools/gn/scope.h"
#include "tools/gn/string_utils.h"
+// Dictionary keys used for JSON-formatted tree dump.
+const char kJsonNodeChild[] = "child";
+const char kJsonNodeType[] = "type";
+const char kJsonNodeValue[] = "value";
+const char kJsonBeforeComment[] = "before_comment";
+const char kJsonSuffixComment[] = "suffix_comment";
+const char kJsonAfterComment[] = "after_comment";
+
namespace {
enum DepsCategory {
@@ -53,10 +62,6 @@
: base::StringPiece());
}
-std::string IndentFor(int value) {
- return std::string(value, ' ');
-}
-
bool IsSortRangeSeparator(const ParseNode* node, const ParseNode* prev) {
// If it's a block comment, or has an attached comment with a blank line
// before it, then we break the range at this point.
@@ -133,15 +138,42 @@
return comments_.get();
}
-void ParseNode::PrintComments(std::ostream& out, int indent) const {
+base::Value ParseNode::CreateJSONNode(const char* type) const {
+ base::Value dict(base::Value::Type::DICTIONARY);
+ dict.SetKey(kJsonNodeType, base::Value(type));
+ AddCommentsJSONNodes(&dict);
+ return dict;
+}
+
+base::Value ParseNode::CreateJSONNode(const char* type,
+ const base::StringPiece& value) const {
+ base::Value dict(base::Value::Type::DICTIONARY);
+ dict.SetKey(kJsonNodeType, base::Value(type));
+ dict.SetKey(kJsonNodeValue, base::Value(value));
+ AddCommentsJSONNodes(&dict);
+ return dict;
+}
+
+void ParseNode::AddCommentsJSONNodes(base::Value* out_value) const {
if (comments_) {
- std::string ind = IndentFor(indent + 1);
- for (const auto& token : comments_->before())
- out << ind << "+BEFORE_COMMENT(\"" << token.value() << "\")\n";
- for (const auto& token : comments_->suffix())
- out << ind << "+SUFFIX_COMMENT(\"" << token.value() << "\")\n";
- for (const auto& token : comments_->after())
- out << ind << "+AFTER_COMMENT(\"" << token.value() << "\")\n";
+ if (comments_->before().size()) {
+ base::Value comment_values(base::Value::Type::LIST);
+ for (const auto& token : comments_->before())
+ comment_values.GetList().push_back(base::Value(token.value()));
+ out_value->SetKey(kJsonBeforeComment, std::move(comment_values));
+ }
+ if (comments_->suffix().size()) {
+ base::Value comment_values(base::Value::Type::LIST);
+ for (const auto& token : comments_->suffix())
+ comment_values.GetList().push_back(base::Value(token.value()));
+ out_value->SetKey(kJsonSuffixComment, std::move(comment_values));
+ }
+ if (comments_->after().size()) {
+ base::Value comment_values(base::Value::Type::LIST);
+ for (const auto& token : comments_->after())
+ comment_values.GetList().push_back(base::Value(token.value()));
+ out_value->SetKey(kJsonAfterComment, std::move(comment_values));
+ }
}
}
@@ -178,14 +210,15 @@
return Err(GetRange(), msg, help);
}
-void AccessorNode::Print(std::ostream& out, int indent) const {
- out << IndentFor(indent) << "ACCESSOR\n";
- PrintComments(out, indent);
- out << IndentFor(indent + 1) << base_.value() << "\n";
+base::Value AccessorNode::GetJSONNode() const {
+ base::Value dict(CreateJSONNode("ACCESSOR", base_.value()));
+ base::Value child(base::Value::Type::LIST);
if (index_)
- index_->Print(out, indent + 1);
+ child.GetList().push_back(index_->GetJSONNode());
else if (member_)
- member_->Print(out, indent + 1);
+ child.GetList().push_back(member_->GetJSONNode());
+ dict.SetKey(kJsonNodeChild, std::move(child));
+ return dict;
}
Value AccessorNode::ExecuteArrayAccess(Scope* scope, Err* err) const {
@@ -311,11 +344,13 @@
return Err(op_, msg, help);
}
-void BinaryOpNode::Print(std::ostream& out, int indent) const {
- out << IndentFor(indent) << "BINARY(" << op_.value() << ")\n";
- PrintComments(out, indent);
- left_->Print(out, indent + 1);
- right_->Print(out, indent + 1);
+base::Value BinaryOpNode::GetJSONNode() const {
+ base::Value dict(CreateJSONNode("BINARY", op_.value()));
+ base::Value child(base::Value::Type::LIST);
+ child.GetList().push_back(left_->GetJSONNode());
+ child.GetList().push_back(right_->GetJSONNode());
+ dict.SetKey(kJsonNodeChild, std::move(child));
+ return dict;
}
// BlockNode ------------------------------------------------------------------
@@ -388,13 +423,16 @@
return Err(GetRange(), msg, help);
}
-void BlockNode::Print(std::ostream& out, int indent) const {
- out << IndentFor(indent) << "BLOCK\n";
- PrintComments(out, indent);
+base::Value BlockNode::GetJSONNode() const {
+ base::Value dict(CreateJSONNode("BLOCK"));
+ base::Value statements(base::Value::Type::LIST);
for (const auto& statement : statements_)
- statement->Print(out, indent + 1);
+ statements.GetList().push_back(statement->GetJSONNode());
if (end_ && end_->comments())
- end_->Print(out, indent + 1);
+ statements.GetList().push_back(end_->GetJSONNode());
+
+ dict.SetKey("child", std::move(statements));
+ return dict;
}
// ConditionNode --------------------------------------------------------------
@@ -441,13 +479,16 @@
return Err(if_token_, msg, help);
}
-void ConditionNode::Print(std::ostream& out, int indent) const {
- out << IndentFor(indent) << "CONDITION\n";
- PrintComments(out, indent);
- condition_->Print(out, indent + 1);
- if_true_->Print(out, indent + 1);
- if (if_false_)
- if_false_->Print(out, indent + 1);
+base::Value ConditionNode::GetJSONNode() const {
+ base::Value dict = CreateJSONNode("CONDITION");
+ base::Value child(base::Value::Type::LIST);
+ child.GetList().push_back(condition_->GetJSONNode());
+ child.GetList().push_back(if_true_->GetJSONNode());
+ if (if_false_) {
+ child.GetList().push_back(if_false_->GetJSONNode());
+ }
+ dict.SetKey(kJsonNodeChild, std::move(child));
+ return std::move(dict);
}
// FunctionCallNode -----------------------------------------------------------
@@ -477,12 +518,15 @@
return Err(function_, msg, help);
}
-void FunctionCallNode::Print(std::ostream& out, int indent) const {
- out << IndentFor(indent) << "FUNCTION(" << function_.value() << ")\n";
- PrintComments(out, indent);
- args_->Print(out, indent + 1);
- if (block_)
- block_->Print(out, indent + 1);
+base::Value FunctionCallNode::GetJSONNode() const {
+ base::Value dict = CreateJSONNode("FUNCTION", function_.value());
+ base::Value child(base::Value::Type::LIST);
+ child.GetList().push_back(args_->GetJSONNode());
+ if (block_) {
+ child.GetList().push_back(block_->GetJSONNode());
+ }
+ dict.SetKey(kJsonNodeChild, std::move(child));
+ return dict;
}
void FunctionCallNode::SetNewLocation(int line_number) {
@@ -539,9 +583,8 @@
return Err(value_, msg, help);
}
-void IdentifierNode::Print(std::ostream& out, int indent) const {
- out << IndentFor(indent) << "IDENTIFIER(" << value_.value() << ")\n";
- PrintComments(out, indent);
+base::Value IdentifierNode::GetJSONNode() const {
+ return CreateJSONNode("IDENTIFIER", value_.value());
}
void IdentifierNode::SetNewLocation(int line_number) {
@@ -589,14 +632,17 @@
return Err(begin_token_, msg, help);
}
-void ListNode::Print(std::ostream& out, int indent) const {
- out << IndentFor(indent) << "LIST" << (prefer_multiline_ ? " multiline" : "")
- << "\n";
- PrintComments(out, indent);
- for (const auto& cur : contents_)
- cur->Print(out, indent + 1);
- if (end_ && end_->comments())
- end_->Print(out, indent + 1);
+base::Value ListNode::GetJSONNode() const {
+ base::Value dict(CreateJSONNode("LIST"));
+ base::Value child(base::Value::Type::LIST);
+ for (const auto& cur : contents_) {
+ child.GetList().push_back(cur->GetJSONNode());
+ }
+ if (end_ && end_->comments()) {
+ child.GetList().push_back(end_->GetJSONNode());
+ }
+ dict.SetKey(kJsonNodeChild, std::move(child));
+ return dict;
}
template <typename Comparator>
@@ -794,9 +840,8 @@
return Err(value_, msg, help);
}
-void LiteralNode::Print(std::ostream& out, int indent) const {
- out << IndentFor(indent) << "LITERAL(" << value_.value() << ")\n";
- PrintComments(out, indent);
+base::Value LiteralNode::GetJSONNode() const {
+ return CreateJSONNode("LITERAL", value_.value());
}
void LiteralNode::SetNewLocation(int line_number) {
@@ -831,10 +876,12 @@
return Err(op_, msg, help);
}
-void UnaryOpNode::Print(std::ostream& out, int indent) const {
- out << IndentFor(indent) << "UNARY(" << op_.value() << ")\n";
- PrintComments(out, indent);
- operand_->Print(out, indent + 1);
+base::Value UnaryOpNode::GetJSONNode() const {
+ base::Value dict = CreateJSONNode("UNARY", op_.value());
+ base::Value child(base::Value::Type::LIST);
+ child.GetList().push_back(operand_->GetJSONNode());
+ dict.SetKey(kJsonNodeChild, std::move(child));
+ return dict;
}
// BlockCommentNode ------------------------------------------------------------
@@ -860,9 +907,10 @@
return Err(comment_, msg, help);
}
-void BlockCommentNode::Print(std::ostream& out, int indent) const {
- out << IndentFor(indent) << "BLOCK_COMMENT(" << comment_.value() << ")\n";
- PrintComments(out, indent);
+base::Value BlockCommentNode::GetJSONNode() const {
+ std::string escaped;
+ base::EscapeJSONString(comment_.value().as_string(), false, &escaped);
+ return CreateJSONNode("BLOCK_COMMENT", escaped);
}
// EndNode ---------------------------------------------------------------------
@@ -888,7 +936,6 @@
return Err(value_, msg, help);
}
-void EndNode::Print(std::ostream& out, int indent) const {
- out << IndentFor(indent) << "END(" << value_.value() << ")\n";
- PrintComments(out, indent);
+base::Value EndNode::GetJSONNode() const {
+ return CreateJSONNode("END", value_.value());
}
diff --git a/tools/gn/parse_tree.h b/tools/gn/parse_tree.h
index 9927307..6c61b72 100644
--- a/tools/gn/parse_tree.h
+++ b/tools/gn/parse_tree.h
@@ -12,6 +12,7 @@
#include <vector>
#include "base/macros.h"
+#include "base/values.h"
#include "tools/gn/err.h"
#include "tools/gn/token.h"
#include "tools/gn/value.h"
@@ -29,6 +30,14 @@
class Scope;
class UnaryOpNode;
+// Dictionary keys used for JSON-formatted tree dump.
+extern const char kJsonNodeChild[];
+extern const char kJsonNodeType[];
+extern const char kJsonNodeValue[];
+extern const char kJsonBeforeComment[];
+extern const char kJsonSuffixComment[];
+extern const char kJsonAfterComment[];
+
class Comments {
public:
Comments();
@@ -92,15 +101,24 @@
const std::string& msg,
const std::string& help = std::string()) const = 0;
- // Prints a representation of this node to the given string, indenting
- // by the given number of spaces.
- virtual void Print(std::ostream& out, int indent) const = 0;
+ // Generates a representation of this node in base::Value, to be used for
+ // exporting the tree as a JSON or formatted text with indents.
+ virtual base::Value GetJSONNode() const = 0;
const Comments* comments() const { return comments_.get(); }
Comments* comments_mutable();
- void PrintComments(std::ostream& out, int indent) const;
+
+ protected:
+ // Helper functions for GetJSONNode. Creates and fills a Value object with
+ // given type (and value).
+ base::Value CreateJSONNode(const char* type) const;
+ base::Value CreateJSONNode(const char* type, const base::StringPiece& value)
+ const;
private:
+ // Helper function for CreateJSONNode.
+ void AddCommentsJSONNodes(base::Value* out_value) const;
+
std::unique_ptr<Comments> comments_;
DISALLOW_COPY_AND_ASSIGN(ParseNode);
@@ -142,7 +160,7 @@
Err MakeErrorDescribing(
const std::string& msg,
const std::string& help = std::string()) const override;
- void Print(std::ostream& out, int indent) const override;
+ base::Value GetJSONNode() const override;
// Base is the thing on the left of the [] or dot, currently always required
// to be an identifier token.
@@ -196,7 +214,7 @@
Err MakeErrorDescribing(
const std::string& msg,
const std::string& help = std::string()) const override;
- void Print(std::ostream& out, int indent) const override;
+ base::Value GetJSONNode() const override;
const Token& op() const { return op_; }
void set_op(const Token& t) { op_ = t; }
@@ -241,7 +259,7 @@
Err MakeErrorDescribing(
const std::string& msg,
const std::string& help = std::string()) const override;
- void Print(std::ostream& out, int indent) const override;
+ base::Value GetJSONNode() const override;
void set_begin_token(const Token& t) { begin_token_ = t; }
void set_end(std::unique_ptr<EndNode> e) { end_ = std::move(e); }
@@ -282,7 +300,7 @@
Err MakeErrorDescribing(
const std::string& msg,
const std::string& help = std::string()) const override;
- void Print(std::ostream& out, int indent) const override;
+ base::Value GetJSONNode() const override;
void set_if_token(const Token& token) { if_token_ = token; }
@@ -323,7 +341,7 @@
Err MakeErrorDescribing(
const std::string& msg,
const std::string& help = std::string()) const override;
- void Print(std::ostream& out, int indent) const override;
+ base::Value GetJSONNode() const override;
const Token& function() const { return function_; }
void set_function(Token t) { function_ = t; }
@@ -358,7 +376,7 @@
Err MakeErrorDescribing(
const std::string& msg,
const std::string& help = std::string()) const override;
- void Print(std::ostream& out, int indent) const override;
+ base::Value GetJSONNode() const override;
const Token& value() const { return value_; }
void set_value(const Token& t) { value_ = t; }
@@ -384,7 +402,7 @@
Err MakeErrorDescribing(
const std::string& msg,
const std::string& help = std::string()) const override;
- void Print(std::ostream& out, int indent) const override;
+ base::Value GetJSONNode() const override;
void set_begin_token(const Token& t) { begin_token_ = t; }
const Token& Begin() const { return begin_token_; }
@@ -446,7 +464,7 @@
Err MakeErrorDescribing(
const std::string& msg,
const std::string& help = std::string()) const override;
- void Print(std::ostream& out, int indent) const override;
+ base::Value GetJSONNode() const override;
const Token& value() const { return value_; }
void set_value(const Token& t) { value_ = t; }
@@ -472,7 +490,7 @@
Err MakeErrorDescribing(
const std::string& msg,
const std::string& help = std::string()) const override;
- void Print(std::ostream& out, int indent) const override;
+ base::Value GetJSONNode() const override;
const Token& op() const { return op_; }
void set_op(const Token& t) { op_ = t; }
@@ -507,7 +525,7 @@
Err MakeErrorDescribing(
const std::string& msg,
const std::string& help = std::string()) const override;
- void Print(std::ostream& out, int indent) const override;
+ base::Value GetJSONNode() const override;
const Token& comment() const { return comment_; }
void set_comment(const Token& t) { comment_ = t; }
@@ -535,7 +553,7 @@
Err MakeErrorDescribing(
const std::string& msg,
const std::string& help = std::string()) const override;
- void Print(std::ostream& out, int indent) const override;
+ base::Value GetJSONNode() const override;
const Token& value() const { return value_; }
void set_value(const Token& t) { value_ = t; }
diff --git a/tools/gn/parser.cc b/tools/gn/parser.cc
index f4f028f..e10ed41 100644
--- a/tools/gn/parser.cc
+++ b/tools/gn/parser.cc
@@ -889,3 +889,49 @@
const_cast<ParseNode*>(*i)->comments_mutable()->ReverseSuffix();
}
}
+
+std::string IndentFor(int value) {
+ return std::string(value, ' ');
+}
+
+void RenderToText(const base::Value& node, int indent_level,
+ std::ostringstream& os) {
+ const base::Value* child = node.FindKey(std::string("child"));
+ std::string node_type(node.FindKey("type")->GetString());
+ if (node_type == "ACCESSOR") {
+ // AccessorNode is a bit special, in that it holds a Token, not a ParseNode
+ // for the base.
+ os << IndentFor(indent_level) << node_type << std::endl;
+ os << IndentFor(indent_level + 1) << node.FindKey("value")->GetString()
+ << std::endl;
+ } else {
+ os << IndentFor(indent_level) << node_type;
+ if (node.FindKey("value")) {
+ os << "(" << node.FindKey("value")->GetString() << ")";
+ }
+ os << std::endl;
+ }
+ if (node.FindKey(kJsonBeforeComment)) {
+ for (auto& v : node.FindKey(kJsonBeforeComment)->GetList()) {
+ os << IndentFor(indent_level + 1) <<
+ "+BEFORE_COMMENT(\"" << v.GetString() << "\")\n";
+ }
+ }
+ if (node.FindKey(kJsonSuffixComment)) {
+ for (auto& v : node.FindKey(kJsonSuffixComment)->GetList()) {
+ os << IndentFor(indent_level + 1) <<
+ "+SUFFIX_COMMENT(\"" << v.GetString() << "\")\n";
+ }
+ }
+ if (node.FindKey(kJsonAfterComment)) {
+ for (auto& v : node.FindKey(kJsonAfterComment)->GetList()) {
+ os << IndentFor(indent_level + 1) <<
+ "+AFTER_COMMENT(\"" << v.GetString() << "\")\n";
+ }
+ }
+ if (child) {
+ for (const base::Value& n : child->GetList()) {
+ RenderToText(n, indent_level + 1, os);
+ }
+ }
+}
diff --git a/tools/gn/parser.h b/tools/gn/parser.h
index c1930b9..5ecbd9d 100644
--- a/tools/gn/parser.h
+++ b/tools/gn/parser.h
@@ -147,4 +147,9 @@
int precedence;
};
+// Renders parse subtree as a formatted text, indenting by the given number of
+// spaces.
+void RenderToText(const base::Value& node, int indent_level,
+ std::ostringstream& os);
+
#endif // TOOLS_GN_PARSER_H_
diff --git a/tools/gn/parser_unittest.cc b/tools/gn/parser_unittest.cc
index e2bbf39..d24067c 100644
--- a/tools/gn/parser_unittest.cc
+++ b/tools/gn/parser_unittest.cc
@@ -32,7 +32,7 @@
ASSERT_TRUE(result);
std::ostringstream collector;
- result->Print(collector, 0);
+ RenderToText(result->GetJSONNode(), 0, collector);
EXPECT_EQ(expected, collector.str());
}
@@ -48,7 +48,7 @@
ASSERT_TRUE(result);
std::ostringstream collector;
- result->Print(collector, 0);
+ RenderToText(result->GetJSONNode(), 0, collector);
EXPECT_EQ(expected, collector.str());
}
diff --git a/tools/gn/setup.cc b/tools/gn/setup.cc
index 6720f65..b9debab 100644
--- a/tools/gn/setup.cc
+++ b/tools/gn/setup.cc
@@ -527,7 +527,7 @@
base::CreateDirectory(build_arg_file.DirName());
std::string contents = args_input_file_->contents();
- commands::FormatStringToString(contents, false, &contents);
+ commands::FormatStringToString(contents, commands::TreeDumpMode::kInactive, &contents);
#if defined(OS_WIN)
// Use Windows lineendings for this file since it will often open in
// Notepad which can't handle Unix ones.