Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(70)

Side by Side Diff: tools/gn/command_format.cc

Issue 591373002: gn: start of format command (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@gn-more-comment-stuff
Patch Set: change 'copyright header block' to 'standard header block' for android_webview/tools/check_licenses… Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tools/gn/BUILD.gn ('k') | tools/gn/command_format_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 "base/command_line.h"
8 #include "tools/gn/commands.h"
9 #include "tools/gn/input_file.h"
10 #include "tools/gn/parser.h"
11 #include "tools/gn/scheduler.h"
12 #include "tools/gn/setup.h"
13 #include "tools/gn/source_file.h"
14 #include "tools/gn/tokenizer.h"
15
16 namespace commands {
17
18 const char kSwitchDumpTree[] = "dump-tree";
19
20 const char kFormat[] = "format";
21 const char kFormat_HelpShort[] =
22 "format: Format .gn file.";
23 const char kFormat_Help[] =
24 "gn format: Format .gn file. (ALPHA, WILL CURRENTLY DESTROY DATA!)\n"
25 "\n"
26 " gn format //some/BUILD.gn\n"
27 " gn format some\\BUILD.gn\n"
28 "\n"
29 " Formats .gn file to a standard format. THIS IS NOT FULLY IMPLEMENTED\n"
30 " YET! IT WILL EAT YOUR BEAUTIFUL .GN FILES. AND YOUR LAUNDRY.\n"
31 " At a minimum, make sure everything is `git commit`d so you can\n"
32 " `git checkout -f` to recover.\n";
33
34 namespace {
35
36 const int kIndentSize = 2;
37
38 class Printer {
39 public:
40 Printer();
41 ~Printer();
42
43 void Block(const ParseNode* file);
44
45 std::string String() const { return output_; }
46
47 private:
48 // Format a list of values using the given style.
49 enum SequenceStyle {
50 kSequenceStyleFunctionCall,
51 kSequenceStyleList,
52 kSequenceStyleBlock,
53 };
54
55 enum ExprStyle {
56 kExprStyleRegular,
57 kExprStyleComment,
58 };
59
60 // Add to output.
61 void Print(base::StringPiece str);
62
63 // Add the current margin (as spaces) to the output.
64 void PrintMargin();
65
66 void TrimAndPrintToken(const Token& token);
67
68 // End the current line, flushing end of line comments.
69 void Newline();
70
71 // Remove trailing spaces from the current line.
72 void Trim();
73
74 // Get the 0-based x position on the current line.
75 int CurrentColumn();
76
77 // Print the expression to the output buffer. Returns the type of element
78 // added to the output.
79 ExprStyle Expr(const ParseNode* root);
80
81 template <class PARSENODE> // Just for const covariance.
82 void Sequence(SequenceStyle style, const std::vector<PARSENODE*>& list);
83
84 std::string output_; // Output buffer.
85 std::vector<Token> comments_; // Pending end-of-line comments.
86 int margin_; // Left margin (number of spaces).
87
88 DISALLOW_COPY_AND_ASSIGN(Printer);
89 };
90
91 Printer::Printer() : margin_(0) {
92 output_.reserve(100 << 10);
93 }
94
95 Printer::~Printer() {
96 }
97
98 void Printer::Print(base::StringPiece str) {
99 str.AppendToString(&output_);
100 }
101
102 void Printer::PrintMargin() {
103 output_ += std::string(margin_, ' ');
104 }
105
106 void Printer::TrimAndPrintToken(const Token& token) {
107 std::string trimmed;
108 TrimWhitespaceASCII(token.value().as_string(), base::TRIM_ALL, &trimmed);
109 Print(trimmed);
110 }
111
112 void Printer::Newline() {
113 if (!comments_.empty()) {
114 Print(" ");
115 int i = 0;
116 for (const auto& c : comments_) {
117 if (i > 0) {
118 Trim();
119 Print("\n");
120 PrintMargin();
121 }
122 TrimAndPrintToken(c);
123 }
124 comments_.clear();
125 }
126 Trim();
127 Print("\n");
128 PrintMargin();
129 }
130
131 void Printer::Trim() {
132 size_t n = output_.size();
133 while (n > 0 && output_[n - 1] == ' ')
134 --n;
135 output_.resize(n);
136 }
137
138 int Printer::CurrentColumn() {
139 int n = 0;
140 while (n < static_cast<int>(output_.size()) &&
141 output_[output_.size() - 1 - n] != '\n') {
142 ++n;
143 }
144 return n;
145 }
146
147 void Printer::Block(const ParseNode* root) {
148 const BlockNode* block = root->AsBlock();
149
150 if (block->comments()) {
151 for (const auto& c : block->comments()->before()) {
152 TrimAndPrintToken(c);
153 Newline();
154 }
155 }
156
157 size_t i = 0;
158 for (const auto& stmt : block->statements()) {
159 Expr(stmt);
160 Newline();
161 if (stmt->comments()) {
162 // Why are before() not printed here too? before() are handled inside
163 // Expr(), as are suffix() which are queued to the next Newline().
164 // However, because it's a general expression handler, it doesn't insert
165 // the newline itself, which only happens between block statements. So,
166 // the after are handled explicitly here.
167 for (const auto& c : stmt->comments()->after()) {
168 TrimAndPrintToken(c);
169 Newline();
170 }
171 }
172 if (i < block->statements().size() - 1)
173 Newline();
174 ++i;
175 }
176
177 if (block->comments()) {
178 for (const auto& c : block->comments()->after()) {
179 TrimAndPrintToken(c);
180 Newline();
181 }
182 }
183 }
184
185 Printer::ExprStyle Printer::Expr(const ParseNode* root) {
186 ExprStyle result = kExprStyleRegular;
187 if (root->comments()) {
188 if (!root->comments()->before().empty()) {
189 Trim();
190 // If there's already other text on the line, start a new line.
191 if (CurrentColumn() > 0)
192 Print("\n");
193 // We're printing a line comment, so we need to be at the current margin.
194 PrintMargin();
195 for (const auto& c : root->comments()->before()) {
196 TrimAndPrintToken(c);
197 Newline();
198 }
199 }
200 }
201
202 if (root->AsAccessor()) {
203 Print("TODO(scottmg): AccessorNode");
204 } else if (const BinaryOpNode* binop = root->AsBinaryOp()) {
205 // TODO(scottmg): Lots to do here for complex if expressions: reflowing,
206 // parenthesizing, etc.
207 Expr(binop->left());
208 Print(" ");
209 Print(binop->op().value());
210 Print(" ");
211 Expr(binop->right());
212 } else if (const BlockNode* block = root->AsBlock()) {
213 Sequence(kSequenceStyleBlock, block->statements());
214 } else if (const ConditionNode* condition = root->AsConditionNode()) {
215 Print("if (");
216 Expr(condition->condition());
217 Print(") {");
218 margin_ += kIndentSize;
219 Newline();
220 Block(condition->if_true());
221 margin_ -= kIndentSize;
222 Trim();
223 PrintMargin();
224 Print("}");
225 if (condition->if_false()) {
226 Print(" else ");
227 // If it's a block it's a bare 'else', otherwise it's an 'else if'. See
228 // ConditionNode::Execute.
229 bool is_else_if = condition->if_false()->AsBlock() == NULL;
230 if (is_else_if) {
231 Expr(condition->if_false());
232 } else {
233 Print("{");
234 margin_ += kIndentSize;
235 Newline();
236 Block(condition->if_false());
237 margin_ -= kIndentSize;
238 Trim();
239 PrintMargin();
240 Print("}");
241 }
242 }
243 } else if (const FunctionCallNode* func_call = root->AsFunctionCall()) {
244 Print(func_call->function().value());
245 Sequence(kSequenceStyleFunctionCall, func_call->args()->contents());
246 Print(" {");
247 margin_ += kIndentSize;
248 Newline();
249 Block(func_call->block());
250 margin_ -= kIndentSize;
251 Trim();
252 PrintMargin();
253 Print("}");
254 } else if (const IdentifierNode* identifier = root->AsIdentifier()) {
255 Print(identifier->value().value());
256 } else if (const ListNode* list = root->AsList()) {
257 Sequence(kSequenceStyleList, list->contents());
258 } else if (const LiteralNode* literal = root->AsLiteral()) {
259 // TODO(scottmg): Quoting?
260 Print(literal->value().value());
261 } else if (const UnaryOpNode* unaryop = root->AsUnaryOp()) {
262 Print(unaryop->op().value());
263 Expr(unaryop->operand());
264 } else if (const BlockCommentNode* block_comment = root->AsBlockComment()) {
265 Print(block_comment->comment().value());
266 result = kExprStyleComment;
267 } else {
268 CHECK(false) << "Unhandled case in Expr.";
269 }
270
271 // Defer any end of line comment until we reach the newline.
272 if (root->comments() && !root->comments()->suffix().empty()) {
273 std::copy(root->comments()->suffix().begin(),
274 root->comments()->suffix().end(),
275 std::back_inserter(comments_));
276 }
277
278 return result;
279 }
280
281 template <class PARSENODE>
282 void Printer::Sequence(SequenceStyle style,
283 const std::vector<PARSENODE*>& list) {
284 bool force_multiline = false;
285 if (style == kSequenceStyleFunctionCall)
286 Print("(");
287 else if (style == kSequenceStyleList)
288 Print("[");
289
290 if (style == kSequenceStyleBlock)
291 force_multiline = true;
292
293 // If there's before line comments, make sure we have a place to put them.
294 for (const auto& i : list) {
295 if (i->comments() && !i->comments()->before().empty())
296 force_multiline = true;
297 }
298
299 if (list.size() == 0 && !force_multiline) {
300 // No elements, and not forcing newlines, print nothing.
301 } else if (list.size() == 1 && !force_multiline) {
302 if (style != kSequenceStyleFunctionCall)
303 Print(" ");
304 Expr(list[0]);
305 CHECK(list[0]->comments()->after().empty());
306 if (style != kSequenceStyleFunctionCall)
307 Print(" ");
308 } else {
309 margin_ += kIndentSize;
310 size_t i = 0;
311 for (const auto& x : list) {
312 Newline();
313 ExprStyle expr_style = Expr(x);
314 CHECK(x->comments()->after().empty());
315 if (i < list.size() - 1 || style == kSequenceStyleList) {
316 if (expr_style == kExprStyleRegular)
317 Print(",");
318 else
319 Newline();
320 }
321 ++i;
322 }
323
324 margin_ -= kIndentSize;
325 Newline();
326 }
327
328 if (style == kSequenceStyleFunctionCall)
329 Print(")");
330 else if (style == kSequenceStyleList)
331 Print("]");
332 }
333
334 } // namespace
335
336 bool FormatFileToString(const std::string& input_filename,
337 bool dump_tree,
338 std::string* output) {
339 Setup setup;
340 Err err;
341 SourceFile input_file(input_filename);
342 const ParseNode* parse_node =
343 setup.scheduler().input_file_manager()->SyncLoadFile(
344 LocationRange(), &setup.build_settings(), input_file, &err);
345 if (err.has_error()) {
346 err.PrintToStdout();
347 return false;
348 }
349 if (dump_tree) {
350 std::ostringstream os;
351 parse_node->Print(os, 0);
352 printf("----------------------\n");
353 printf("-- PARSE TREE --------\n");
354 printf("----------------------\n");
355 printf("%s", os.str().c_str());
356 printf("----------------------\n");
357 }
358 Printer pr;
359 pr.Block(parse_node);
360 *output = pr.String();
361 return true;
362 }
363
364 int RunFormat(const std::vector<std::string>& args) {
365 // TODO(scottmg): Eventually, this should be a list/spec of files, and they
366 // should all be done in parallel and in-place. For now, we don't want to
367 // overwrite good data with mistakenly reformatted stuff, so we just simply
368 // print the formatted output to stdout.
369 if (args.size() != 1) {
370 Err(Location(), "Expecting exactly one argument, see `gn help format`.\n")
371 .PrintToStdout();
372 return 1;
373 }
374
375 bool dump_tree =
376 base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDumpTree);
377
378 std::string input_name = args[0];
379 if (input_name[0] != '/') {
380 std::replace(input_name.begin(), input_name.end(), '\\', '/');
381 input_name = "//" + input_name;
382 }
383 std::string output_string;
384 if (FormatFileToString(input_name, dump_tree, &output_string)) {
385 printf("%s", output_string.c_str());
386 }
387
388 return 0;
389 }
390
391 } // namespace commands
OLDNEW
« no previous file with comments | « tools/gn/BUILD.gn ('k') | tools/gn/command_format_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698