1 // Copyright (c) 2013 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.
5 #include "tools/gn/standard_out.h"
9 #include "base/command_line.h"
10 #include "base/logging.h"
11 #include "base/strings/string_split.h"
12 #include "build/build_config.h"
13 #include "tools/gn/switches.h"
24 bool initialized
= false;
28 WORD default_attributes
;
30 bool is_console
= false;
32 bool is_markdown
= false;
34 void EnsureInitialized() {
39 const base::CommandLine
* cmdline
= base::CommandLine::ForCurrentProcess();
40 if (cmdline
->HasSwitch(switches::kMarkdown
)) {
41 // Output help in Markdown's syntax, not color-highlighted.
45 if (cmdline
->HasSwitch(switches::kNoColor
)) {
52 // On Windows, we can't force the color on. If the output handle isn't a
53 // console, there's nothing we can do about it.
54 hstdout
= ::GetStdHandle(STD_OUTPUT_HANDLE
);
55 CONSOLE_SCREEN_BUFFER_INFO info
;
56 is_console
= !!::GetConsoleScreenBufferInfo(hstdout
, &info
);
57 default_attributes
= info
.wAttributes
;
59 if (cmdline
->HasSwitch(switches::kColor
))
62 is_console
= isatty(fileno(stdout
));
67 void WriteToStdOut(const std::string
& output
) {
68 size_t written_bytes
= fwrite(output
.data(), 1, output
.size(), stdout
);
69 DCHECK_EQ(output
.size(), written_bytes
);
71 #endif // !defined(OS_WIN)
73 void OutputMarkdownDec(TextDecoration dec
) {
74 // The markdown rendering turns "dim" text to italics and any
75 // other colored text to bold.
79 if (dec
== DECORATION_DIM
)
80 ::WriteFile(hstdout
, "*", 1, &written
, nullptr);
81 else if (dec
!= DECORATION_NONE
)
82 ::WriteFile(hstdout
, "**", 2, &written
, nullptr);
84 if (dec
== DECORATION_DIM
)
86 else if (dec
!= DECORATION_NONE
)
95 void OutputString(const std::string
& output
, TextDecoration dec
) {
100 OutputMarkdownDec(dec
);
101 } else if (is_console
) {
103 case DECORATION_NONE
:
106 ::SetConsoleTextAttribute(hstdout
, FOREGROUND_INTENSITY
);
109 ::SetConsoleTextAttribute(hstdout
,
110 FOREGROUND_RED
| FOREGROUND_INTENSITY
);
112 case DECORATION_GREEN
:
113 // Keep green non-bold.
114 ::SetConsoleTextAttribute(hstdout
, FOREGROUND_GREEN
);
116 case DECORATION_BLUE
:
117 ::SetConsoleTextAttribute(hstdout
,
118 FOREGROUND_BLUE
| FOREGROUND_INTENSITY
);
120 case DECORATION_YELLOW
:
121 ::SetConsoleTextAttribute(hstdout
,
122 FOREGROUND_RED
| FOREGROUND_GREEN
);
127 ::WriteFile(hstdout
, output
.c_str(), static_cast<DWORD
>(output
.size()),
131 OutputMarkdownDec(dec
);
132 } else if (is_console
) {
133 ::SetConsoleTextAttribute(hstdout
, default_attributes
);
139 void OutputString(const std::string
& output
, TextDecoration dec
) {
142 OutputMarkdownDec(dec
);
143 } else if (is_console
) {
145 case DECORATION_NONE
:
148 WriteToStdOut("\e[2m");
151 WriteToStdOut("\e[31m\e[1m");
153 case DECORATION_GREEN
:
154 WriteToStdOut("\e[32m");
156 case DECORATION_BLUE
:
157 WriteToStdOut("\e[34m\e[1m");
159 case DECORATION_YELLOW
:
160 WriteToStdOut("\e[33m\e[1m");
165 WriteToStdOut(output
.data());
168 OutputMarkdownDec(dec
);
169 } else if (is_console
&& dec
!= DECORATION_NONE
) {
170 WriteToStdOut("\e[0m");
176 void PrintShortHelp(const std::string
& line
) {
179 size_t colon_offset
= line
.find(':');
180 size_t first_normal
= 0;
181 if (colon_offset
!= std::string::npos
) {
182 OutputString(" " + line
.substr(0, colon_offset
), DECORATION_YELLOW
);
183 first_normal
= colon_offset
;
186 // See if the colon is followed by a " [" and if so, dim the contents of [ ].
187 if (first_normal
> 0 &&
188 line
.size() > first_normal
+ 2 &&
189 line
[first_normal
+ 1] == ' ' && line
[first_normal
+ 2] == '[') {
190 size_t begin_bracket
= first_normal
+ 2;
192 first_normal
= line
.find(']', begin_bracket
);
193 if (first_normal
== std::string::npos
)
194 first_normal
= line
.size();
197 OutputString(line
.substr(begin_bracket
, first_normal
- begin_bracket
),
201 OutputString(line
.substr(first_normal
) + "\n");
204 void PrintLongHelp(const std::string
& text
) {
207 bool first_header
= true;
208 bool in_body
= false;
209 for (const std::string
& line
: base::SplitString(
210 text
, "\n", base::KEEP_WHITESPACE
, base::SPLIT_WANT_ALL
)) {
211 // Check for a heading line.
212 if (!line
.empty() && line
[0] != ' ') {
214 // GN's block-level formatting is converted to markdown as follows:
215 // * The first heading is treated as an H2.
216 // * Subsequent heading are treated as H3s.
217 // * Any other text is wrapped in a code block and displayed as-is.
219 // Span-level formatting (the decorations) is converted inside
222 OutputString("```\n\n", DECORATION_NONE
);
227 OutputString("## ", DECORATION_NONE
);
228 first_header
= false;
230 OutputString("### ", DECORATION_NONE
);
234 // Highlight up to the colon (if any).
235 size_t chars_to_highlight
= line
.find(':');
236 if (chars_to_highlight
== std::string::npos
)
237 chars_to_highlight
= line
.size();
239 OutputString(line
.substr(0, chars_to_highlight
), DECORATION_YELLOW
);
240 OutputString(line
.substr(chars_to_highlight
) + "\n");
242 } else if (is_markdown
&& !line
.empty() && !in_body
) {
243 OutputString("```\n", DECORATION_NONE
);
247 // Check for a comment.
248 TextDecoration dec
= DECORATION_NONE
;
249 for (const auto& elem
: line
) {
250 if (elem
== '#' && !is_markdown
) {
251 // Got a comment, draw dimmed.
252 dec
= DECORATION_DIM
;
254 } else if (elem
!= ' ') {
259 OutputString(line
+ "\n", dec
);
262 if (is_markdown
&& in_body
)
263 OutputString("\n```\n");