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
));
66 void WriteToStdOut(const std::string
& output
) {
67 size_t written_bytes
= fwrite(output
.data(), 1, output
.size(), stdout
);
68 DCHECK_EQ(output
.size(), written_bytes
);
71 void OutputMarkdownDec(TextDecoration dec
) {
72 // The markdown rendering turns "dim" text to italics and any
73 // other colored text to bold.
77 if (dec
== DECORATION_DIM
)
78 ::WriteFile(hstdout
, "*", 1, &written
, nullptr);
79 else if (dec
!= DECORATION_NONE
)
80 ::WriteFile(hstdout
, "**", 2, &written
, nullptr);
82 if (dec
== DECORATION_DIM
)
84 else if (dec
!= DECORATION_NONE
)
93 void OutputString(const std::string
& output
, TextDecoration dec
) {
98 OutputMarkdownDec(dec
);
99 } else if (is_console
) {
101 case DECORATION_NONE
:
104 ::SetConsoleTextAttribute(hstdout
, FOREGROUND_INTENSITY
);
107 ::SetConsoleTextAttribute(hstdout
,
108 FOREGROUND_RED
| FOREGROUND_INTENSITY
);
110 case DECORATION_GREEN
:
111 // Keep green non-bold.
112 ::SetConsoleTextAttribute(hstdout
, FOREGROUND_GREEN
);
114 case DECORATION_BLUE
:
115 ::SetConsoleTextAttribute(hstdout
,
116 FOREGROUND_BLUE
| FOREGROUND_INTENSITY
);
118 case DECORATION_YELLOW
:
119 ::SetConsoleTextAttribute(hstdout
,
120 FOREGROUND_RED
| FOREGROUND_GREEN
);
125 ::WriteFile(hstdout
, output
.c_str(), static_cast<DWORD
>(output
.size()),
129 OutputMarkdownDec(dec
);
130 } else if (is_console
) {
131 ::SetConsoleTextAttribute(hstdout
, default_attributes
);
137 void OutputString(const std::string
& output
, TextDecoration dec
) {
140 OutputMarkdownDec(dec
);
141 } else if (is_console
) {
143 case DECORATION_NONE
:
146 WriteToStdOut("\e[2m");
149 WriteToStdOut("\e[31m\e[1m");
151 case DECORATION_GREEN
:
152 WriteToStdOut("\e[32m");
154 case DECORATION_BLUE
:
155 WriteToStdOut("\e[34m\e[1m");
157 case DECORATION_YELLOW
:
158 WriteToStdOut("\e[33m\e[1m");
163 WriteToStdOut(output
.data());
166 OutputMarkdownDec(dec
);
167 } else if (is_console
&& dec
!= DECORATION_NONE
) {
168 WriteToStdOut("\e[0m");
174 void PrintShortHelp(const std::string
& line
) {
177 size_t colon_offset
= line
.find(':');
178 size_t first_normal
= 0;
179 if (colon_offset
!= std::string::npos
) {
180 OutputString(" " + line
.substr(0, colon_offset
), DECORATION_YELLOW
);
181 first_normal
= colon_offset
;
184 // See if the colon is followed by a " [" and if so, dim the contents of [ ].
185 if (first_normal
> 0 &&
186 line
.size() > first_normal
+ 2 &&
187 line
[first_normal
+ 1] == ' ' && line
[first_normal
+ 2] == '[') {
188 size_t begin_bracket
= first_normal
+ 2;
190 first_normal
= line
.find(']', begin_bracket
);
191 if (first_normal
== std::string::npos
)
192 first_normal
= line
.size();
195 OutputString(line
.substr(begin_bracket
, first_normal
- begin_bracket
),
199 OutputString(line
.substr(first_normal
) + "\n");
202 void PrintLongHelp(const std::string
& text
) {
205 std::vector
<std::string
> lines
;
206 base::SplitStringDontTrim(text
, '\n', &lines
);
208 bool first_header
= true;
209 bool in_body
= false;
210 for (const auto& line
: lines
) {
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");