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_piece.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/string_util.h"
14 #include "build/build_config.h"
15 #include "tools/gn/switches.h"
26 bool initialized
= false;
30 WORD default_attributes
;
32 bool is_console
= false;
34 bool is_markdown
= false;
36 void EnsureInitialized() {
41 const base::CommandLine
* cmdline
= base::CommandLine::ForCurrentProcess();
42 if (cmdline
->HasSwitch(switches::kMarkdown
)) {
43 // Output help in Markdown's syntax, not color-highlighted.
47 if (cmdline
->HasSwitch(switches::kNoColor
)) {
54 // On Windows, we can't force the color on. If the output handle isn't a
55 // console, there's nothing we can do about it.
56 hstdout
= ::GetStdHandle(STD_OUTPUT_HANDLE
);
57 CONSOLE_SCREEN_BUFFER_INFO info
;
58 is_console
= !!::GetConsoleScreenBufferInfo(hstdout
, &info
);
59 default_attributes
= info
.wAttributes
;
61 if (cmdline
->HasSwitch(switches::kColor
))
64 is_console
= isatty(fileno(stdout
));
69 void WriteToStdOut(const std::string
& output
) {
70 size_t written_bytes
= fwrite(output
.data(), 1, output
.size(), stdout
);
71 DCHECK_EQ(output
.size(), written_bytes
);
73 #endif // !defined(OS_WIN)
75 void OutputMarkdownDec(TextDecoration dec
) {
76 // The markdown rendering turns "dim" text to italics and any
77 // other colored text to bold.
81 if (dec
== DECORATION_DIM
)
82 ::WriteFile(hstdout
, "*", 1, &written
, nullptr);
83 else if (dec
!= DECORATION_NONE
)
84 ::WriteFile(hstdout
, "**", 2, &written
, nullptr);
86 if (dec
== DECORATION_DIM
)
88 else if (dec
!= DECORATION_NONE
)
97 void OutputString(const std::string
& output
, TextDecoration dec
) {
102 OutputMarkdownDec(dec
);
103 } else if (is_console
) {
105 case DECORATION_NONE
:
108 ::SetConsoleTextAttribute(hstdout
, FOREGROUND_INTENSITY
);
111 ::SetConsoleTextAttribute(hstdout
,
112 FOREGROUND_RED
| FOREGROUND_INTENSITY
);
114 case DECORATION_GREEN
:
115 // Keep green non-bold.
116 ::SetConsoleTextAttribute(hstdout
, FOREGROUND_GREEN
);
118 case DECORATION_BLUE
:
119 ::SetConsoleTextAttribute(hstdout
,
120 FOREGROUND_BLUE
| FOREGROUND_INTENSITY
);
122 case DECORATION_YELLOW
:
123 ::SetConsoleTextAttribute(hstdout
,
124 FOREGROUND_RED
| FOREGROUND_GREEN
);
129 std::string tmpstr
= output
;
130 if (is_markdown
&& dec
== DECORATION_YELLOW
) {
131 // https://code.google.com/p/gitiles/issues/detail?id=77
132 // Gitiles will replace "--" with an em dash in non-code text.
133 // Figuring out all instances of this might be difficult, but we can
134 // at least escape the instances where this shows up in a heading.
135 base::ReplaceSubstringsAfterOffset(&tmpstr
, 0, "--", "\\--");
137 ::WriteFile(hstdout
, tmpstr
.c_str(), static_cast<DWORD
>(tmpstr
.size()),
141 OutputMarkdownDec(dec
);
142 } else if (is_console
) {
143 ::SetConsoleTextAttribute(hstdout
, default_attributes
);
149 void OutputString(const std::string
& output
, TextDecoration dec
) {
152 OutputMarkdownDec(dec
);
153 } else if (is_console
) {
155 case DECORATION_NONE
:
158 WriteToStdOut("\e[2m");
161 WriteToStdOut("\e[31m\e[1m");
163 case DECORATION_GREEN
:
164 WriteToStdOut("\e[32m");
166 case DECORATION_BLUE
:
167 WriteToStdOut("\e[34m\e[1m");
169 case DECORATION_YELLOW
:
170 WriteToStdOut("\e[33m\e[1m");
175 std::string tmpstr
= output
;
176 if (is_markdown
&& dec
== DECORATION_YELLOW
) {
177 // https://code.google.com/p/gitiles/issues/detail?id=77
178 // Gitiles will replace "--" with an em dash in non-code text.
179 // Figuring out all instances of this might be difficult, but we can
180 // at least escape the instances where this shows up in a heading.
181 base::ReplaceSubstringsAfterOffset(&tmpstr
, 0, "--", "\\--");
183 WriteToStdOut(tmpstr
.data());
186 OutputMarkdownDec(dec
);
187 } else if (is_console
&& dec
!= DECORATION_NONE
) {
188 WriteToStdOut("\e[0m");
194 void PrintShortHelp(const std::string
& line
) {
197 size_t colon_offset
= line
.find(':');
198 size_t first_normal
= 0;
199 if (colon_offset
!= std::string::npos
) {
200 OutputString(" " + line
.substr(0, colon_offset
), DECORATION_YELLOW
);
201 first_normal
= colon_offset
;
204 // See if the colon is followed by a " [" and if so, dim the contents of [ ].
205 if (first_normal
> 0 &&
206 line
.size() > first_normal
+ 2 &&
207 line
[first_normal
+ 1] == ' ' && line
[first_normal
+ 2] == '[') {
208 size_t begin_bracket
= first_normal
+ 2;
210 first_normal
= line
.find(']', begin_bracket
);
211 if (first_normal
== std::string::npos
)
212 first_normal
= line
.size();
215 OutputString(line
.substr(begin_bracket
, first_normal
- begin_bracket
),
219 OutputString(line
.substr(first_normal
) + "\n");
222 void PrintLongHelp(const std::string
& text
) {
225 bool first_header
= true;
226 bool in_body
= false;
227 for (const std::string
& line
: base::SplitString(
228 text
, "\n", base::KEEP_WHITESPACE
, base::SPLIT_WANT_ALL
)) {
229 // Check for a heading line.
230 if (!line
.empty() && line
[0] != ' ') {
232 // GN's block-level formatting is converted to markdown as follows:
233 // * The first heading is treated as an H2.
234 // * Subsequent heading are treated as H3s.
235 // * Any other text is wrapped in a code block and displayed as-is.
237 // Span-level formatting (the decorations) is converted inside
240 OutputString("```\n\n", DECORATION_NONE
);
245 OutputString("## ", DECORATION_NONE
);
246 first_header
= false;
248 OutputString("### ", DECORATION_NONE
);
252 // Highlight up to the colon (if any).
253 size_t chars_to_highlight
= line
.find(':');
254 if (chars_to_highlight
== std::string::npos
)
255 chars_to_highlight
= line
.size();
257 OutputString(line
.substr(0, chars_to_highlight
), DECORATION_YELLOW
);
258 OutputString(line
.substr(chars_to_highlight
) + "\n");
260 } else if (is_markdown
&& !line
.empty() && !in_body
) {
261 OutputString("```\n", DECORATION_NONE
);
265 // Check for a comment.
266 TextDecoration dec
= DECORATION_NONE
;
267 for (const auto& elem
: line
) {
268 if (elem
== '#' && !is_markdown
) {
269 // Got a comment, draw dimmed.
270 dec
= DECORATION_DIM
;
272 } else if (elem
!= ' ') {
277 OutputString(line
+ "\n", dec
);
280 if (is_markdown
&& in_body
)
281 OutputString("\n```\n");