Implement diagnostic color printing
For linux and windows consoles. Still needs hooking up to `samples/main.cc` Bug: tint:282 Change-Id: If8430572708ea7d8788ef05d5379886be89fcb17 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/31564 Reviewed-by: dan sinclair <dsinclair@chromium.org> Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
2d89d98fe1
commit
2e6cc992c8
11
BUILD.gn
11
BUILD.gn
|
@ -381,6 +381,8 @@ source_set("libtint_core_src") {
|
|||
"src/diagnostic/diagnostic.h",
|
||||
"src/diagnostic/formatter.cc",
|
||||
"src/diagnostic/formatter.h",
|
||||
"src/diagnostic/printer.cc",
|
||||
"src/diagnostic/printer.h",
|
||||
"src/inspector/entry_point.cc",
|
||||
"src/inspector/entry_point.h",
|
||||
"src/inspector/inspector.cc",
|
||||
|
@ -418,6 +420,14 @@ source_set("libtint_core_src") {
|
|||
"src/writer/writer.h",
|
||||
]
|
||||
|
||||
if (is_linux) {
|
||||
sources += [ "src/diagnostic/printer_linux.cc" ]
|
||||
} else if (is_win) {
|
||||
sources += [ "src/diagnostic/printer_windows.cc" ]
|
||||
} else {
|
||||
sources += [ "src/diagnostic/printer_other.cc" ]
|
||||
}
|
||||
|
||||
public_deps = [
|
||||
":tint_core_enums_unified1",
|
||||
":tint_core_tables_unified1",
|
||||
|
@ -772,6 +782,7 @@ source_set("tint_unittests_core_src") {
|
|||
"src/ast/variable_test.cc",
|
||||
"src/ast/workgroup_decoration_test.cc",
|
||||
"src/diagnostic/formatter_test.cc",
|
||||
"src/diagnostic/printer_test.cc",
|
||||
"src/inspector/inspector_test.cc",
|
||||
"src/scope_stack_test.cc",
|
||||
"src/transform/bound_array_accessors_transform_test.cc",
|
||||
|
|
|
@ -202,6 +202,8 @@ set(TINT_LIB_SRCS
|
|||
diagnostic/diagnostic.h
|
||||
diagnostic/formatter.cc
|
||||
diagnostic/formatter.h
|
||||
diagnostic/printer.cc
|
||||
diagnostic/printer.h
|
||||
inspector/entry_point.cc
|
||||
inspector/entry_point.h
|
||||
inspector/inspector.cc
|
||||
|
@ -239,6 +241,14 @@ set(TINT_LIB_SRCS
|
|||
writer/writer.h
|
||||
)
|
||||
|
||||
if(UNIX)
|
||||
list(APPEND TINT_LIB_SRCS diagnostic/printer_linux.cc)
|
||||
elseif(WIN32)
|
||||
list(APPEND TINT_LIB_SRCS diagnostic/printer_windows.cc)
|
||||
else()
|
||||
list(APPEND TINT_LIB_SRCS diagnostic/printer_other.cc)
|
||||
endif()
|
||||
|
||||
if(${TINT_BUILD_SPV_READER})
|
||||
list(APPEND TINT_LIB_SRCS
|
||||
reader/spirv/construct.h
|
||||
|
@ -382,6 +392,7 @@ set(TINT_TEST_SRCS
|
|||
ast/variable_test.cc
|
||||
ast/workgroup_decoration_test.cc
|
||||
diagnostic/formatter_test.cc
|
||||
diagnostic/printer_test.cc
|
||||
inspector/inspector_test.cc
|
||||
scope_stack_test.cc
|
||||
transform/bound_array_accessors_transform_test.cc
|
||||
|
|
|
@ -59,76 +59,137 @@ std::basic_ostream<CharT, Traits>& operator<<(
|
|||
|
||||
class BasicFormatter : public Formatter {
|
||||
public:
|
||||
struct State {
|
||||
explicit State(Printer* p) : printer(p) {}
|
||||
~State() { flush(); }
|
||||
|
||||
// set_style() sets the current style to new_style, flushing any pending
|
||||
// messages to the printer if the style changed.
|
||||
void set_style(const Style& new_style) {
|
||||
if (style.color != new_style.color || style.bold != new_style.bold) {
|
||||
flush();
|
||||
style = new_style;
|
||||
}
|
||||
}
|
||||
|
||||
// flush() writes any pending messages to the printer, clearing the buffer.
|
||||
void flush() {
|
||||
auto str = stream.str();
|
||||
if (str.length() > 0) {
|
||||
printer->write(str, style);
|
||||
std::stringstream reset;
|
||||
stream.swap(reset);
|
||||
}
|
||||
}
|
||||
|
||||
// operator<<() queues msg to be written to the printer.
|
||||
template <typename T>
|
||||
State& operator<<(const T& msg) {
|
||||
stream << msg;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// newline() queues a newline to be written to the printer.
|
||||
void newline() { stream << std::endl; }
|
||||
|
||||
// repeat() queues the character c to be writen to the printer n times.
|
||||
void repeat(char c, size_t n) {
|
||||
while (n-- > 0) {
|
||||
stream << c;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Printer* printer;
|
||||
Style style;
|
||||
std::stringstream stream;
|
||||
};
|
||||
|
||||
BasicFormatter(bool print_file, bool print_severity, bool print_line)
|
||||
: print_file_(print_file),
|
||||
print_severity_(print_severity),
|
||||
print_line_(print_line) {}
|
||||
|
||||
std::string format(const List& list) const override {
|
||||
void format(const List& list, Printer* printer) const override {
|
||||
State state{printer};
|
||||
|
||||
bool first = true;
|
||||
std::stringstream ss;
|
||||
for (auto diag : list) {
|
||||
state.set_style({});
|
||||
if (!first) {
|
||||
ss << std::endl;
|
||||
state.newline();
|
||||
}
|
||||
format(diag, ss);
|
||||
format(diag, &state);
|
||||
first = false;
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
private:
|
||||
void format(const Diagnostic& diag, std::stringstream& ss) const {
|
||||
void format(const Diagnostic& diag, State* state) const {
|
||||
auto const& src = diag.source;
|
||||
auto const& rng = src.range;
|
||||
|
||||
state->set_style({Color::kDefault, true});
|
||||
|
||||
if (print_file_ && src.file != nullptr && !src.file->path.empty()) {
|
||||
ss << src.file->path;
|
||||
(*state) << src.file->path;
|
||||
if (rng.begin.line > 0) {
|
||||
ss << ":" << rng.begin;
|
||||
(*state) << ":" << rng.begin;
|
||||
}
|
||||
} else {
|
||||
ss << rng.begin;
|
||||
(*state) << rng.begin;
|
||||
}
|
||||
if (print_severity_) {
|
||||
ss << " " << diag.severity;
|
||||
switch (diag.severity) {
|
||||
case Severity::Warning:
|
||||
state->set_style({Color::kYellow, true});
|
||||
break;
|
||||
case Severity::Error:
|
||||
case Severity::Fatal:
|
||||
state->set_style({Color::kRed, true});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
(*state) << " " << diag.severity;
|
||||
}
|
||||
ss << ": " << diag.message;
|
||||
|
||||
state->set_style({Color::kDefault, true});
|
||||
(*state) << ": " << diag.message;
|
||||
|
||||
if (print_line_ && src.file != nullptr && rng.begin.line > 0) {
|
||||
ss << std::endl;
|
||||
state->newline();
|
||||
state->set_style({Color::kDefault, false});
|
||||
|
||||
for (size_t line = rng.begin.line; line <= rng.end.line; line++) {
|
||||
if (line < src.file->lines.size() + 1) {
|
||||
auto len = src.file->lines[line - 1].size();
|
||||
|
||||
ss << src.file->lines[line - 1];
|
||||
ss << std::endl;
|
||||
(*state) << src.file->lines[line - 1];
|
||||
state->newline();
|
||||
state->set_style({Color::kCyan, false});
|
||||
|
||||
if (line == rng.begin.line && line == rng.end.line) {
|
||||
// Single line
|
||||
repeat(' ', rng.begin.column - 1, ss);
|
||||
repeat('^', std::max<size_t>(rng.end.column - rng.begin.column, 1),
|
||||
ss);
|
||||
state->repeat(' ', rng.begin.column - 1);
|
||||
state->repeat(
|
||||
'^', std::max<size_t>(rng.end.column - rng.begin.column, 1));
|
||||
} else if (line == rng.begin.line) {
|
||||
// Start of multi-line
|
||||
repeat(' ', rng.begin.column - 1, ss);
|
||||
repeat('^', len - (rng.begin.column - 1), ss);
|
||||
state->repeat(' ', rng.begin.column - 1);
|
||||
state->repeat('^', len - (rng.begin.column - 1));
|
||||
} else if (line == rng.end.line) {
|
||||
// End of multi-line
|
||||
repeat('^', rng.end.column - 1, ss);
|
||||
state->repeat('^', rng.end.column - 1);
|
||||
} else {
|
||||
// Middle of multi-line
|
||||
repeat('^', len, ss);
|
||||
state->repeat('^', len);
|
||||
}
|
||||
ss << std::endl;
|
||||
state->newline();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void repeat(char c, size_t n, std::stringstream& ss) const {
|
||||
while (n-- > 0) {
|
||||
ss << c;
|
||||
state->set_style({});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "src/diagnostic/printer.h"
|
||||
|
||||
namespace tint {
|
||||
namespace diag {
|
||||
|
||||
|
@ -37,8 +39,18 @@ class Formatter {
|
|||
|
||||
virtual ~Formatter();
|
||||
|
||||
/// @return the human readable list of diagnostics formatted to a string.
|
||||
virtual std::string format(const List&) const = 0;
|
||||
/// format prints the formatted diagnostic list |list| to |printer|.
|
||||
/// @param list the list of diagnostic messages to format
|
||||
/// @param printer the printer used to display the formatted diagnostics
|
||||
virtual void format(const List& list, Printer* printer) const = 0;
|
||||
|
||||
/// @return the list of diagnostics |list| formatted to a string.
|
||||
/// @param list the list of diagnostic messages to format
|
||||
inline std::string format(const List& list) const {
|
||||
StringPrinter printer;
|
||||
format(list, &printer);
|
||||
return printer.str();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace diag
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2020 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/diagnostic/printer.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace tint {
|
||||
namespace diag {
|
||||
|
||||
Printer::~Printer() = default;
|
||||
|
||||
StringPrinter::StringPrinter() = default;
|
||||
StringPrinter::~StringPrinter() = default;
|
||||
|
||||
std::string StringPrinter::str() const {
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
void StringPrinter::write(const std::string& str, const Style&) {
|
||||
stream << str;
|
||||
}
|
||||
|
||||
} // namespace diag
|
||||
} // namespace tint
|
|
@ -0,0 +1,83 @@
|
|||
// Copyright 2020 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SRC_DIAGNOSTIC_PRINTER_H_
|
||||
#define SRC_DIAGNOSTIC_PRINTER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace tint {
|
||||
namespace diag {
|
||||
|
||||
class List;
|
||||
|
||||
/// Color is an enumerator of colors used by Style.
|
||||
enum class Color {
|
||||
kDefault,
|
||||
kBlack,
|
||||
kRed,
|
||||
kGreen,
|
||||
kYellow,
|
||||
kBlue,
|
||||
kMagenta,
|
||||
kCyan,
|
||||
kWhite,
|
||||
};
|
||||
|
||||
/// Style describes how a diagnostic message should be printed.
|
||||
struct Style {
|
||||
/// The foreground text color
|
||||
Color color = Color::kDefault;
|
||||
/// If true the text will be displayed with a strong weight
|
||||
bool bold = false;
|
||||
};
|
||||
|
||||
/// Printers are used to print formatted diagnostic messages to a terminal.
|
||||
class Printer {
|
||||
public:
|
||||
/// @returns a diagnostic Printer
|
||||
/// @param out the file to print to.
|
||||
/// @param use_colors if true, the printer will use colors if |out| is a
|
||||
/// terminal and supports them.
|
||||
static std::unique_ptr<Printer> create(FILE* out, bool use_colors);
|
||||
|
||||
virtual ~Printer();
|
||||
|
||||
/// writes the string str to the printer with the given style.
|
||||
/// @param str the string to write to the printer
|
||||
/// @param style the style used to print |str|
|
||||
virtual void write(const std::string& str, const Style& style) = 0;
|
||||
};
|
||||
|
||||
/// StringPrinter is an implementation of Printer that writes to a std::string.
|
||||
class StringPrinter : public Printer {
|
||||
public:
|
||||
StringPrinter();
|
||||
~StringPrinter() override;
|
||||
|
||||
/// @returns the printed string.
|
||||
std::string str() const;
|
||||
|
||||
void write(const std::string& str, const Style&) override;
|
||||
|
||||
private:
|
||||
std::stringstream stream;
|
||||
};
|
||||
|
||||
} // namespace diag
|
||||
} // namespace tint
|
||||
|
||||
#endif // SRC_DIAGNOSTIC_PRINTER_H_
|
|
@ -0,0 +1,100 @@
|
|||
// Copyright 2020 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/diagnostic/printer.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace tint {
|
||||
namespace diag {
|
||||
namespace {
|
||||
|
||||
bool supports_colors(FILE* f) {
|
||||
if (!isatty(fileno(f))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* cterm = getenv("TERM");
|
||||
if (cterm == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string term = getenv("TERM");
|
||||
if (term != "cygwin" && term != "linux" && term != "rxvt-unicode-256color" &&
|
||||
term != "rxvt-unicode" && term != "screen-256color" && term != "screen" &&
|
||||
term != "tmux-256color" && term != "tmux" && term != "xterm-256color" &&
|
||||
term != "xterm-color" && term != "xterm") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class PrinterLinux : public Printer {
|
||||
public:
|
||||
PrinterLinux(FILE* f, bool colors)
|
||||
: file(f), use_colors(colors && supports_colors(f)) {}
|
||||
|
||||
void write(const std::string& str, const Style& style) override {
|
||||
write_color(style.color, style.bold);
|
||||
fwrite(str.data(), 1, str.size(), file);
|
||||
write_color(Color::kDefault, false);
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr const char* color_code(Color color, bool bold) {
|
||||
switch (color) {
|
||||
case Color::kDefault:
|
||||
return bold ? "\u001b[1m" : "\u001b[0m";
|
||||
case Color::kBlack:
|
||||
return bold ? "\u001b[30;1m" : "\u001b[30m";
|
||||
case Color::kRed:
|
||||
return bold ? "\u001b[31;1m" : "\u001b[31m";
|
||||
case Color::kGreen:
|
||||
return bold ? "\u001b[32;1m" : "\u001b[32m";
|
||||
case Color::kYellow:
|
||||
return bold ? "\u001b[33;1m" : "\u001b[33m";
|
||||
case Color::kBlue:
|
||||
return bold ? "\u001b[34;1m" : "\u001b[34m";
|
||||
case Color::kMagenta:
|
||||
return bold ? "\u001b[35;1m" : "\u001b[35m";
|
||||
case Color::kCyan:
|
||||
return bold ? "\u001b[36;1m" : "\u001b[36m";
|
||||
case Color::kWhite:
|
||||
return bold ? "\u001b[37;1m" : "\u001b[37m";
|
||||
}
|
||||
return ""; // unreachable
|
||||
}
|
||||
|
||||
void write_color(Color color, bool bold) {
|
||||
if (use_colors) {
|
||||
auto* code = color_code(color, bold);
|
||||
fwrite(code, 1, strlen(code), file);
|
||||
}
|
||||
}
|
||||
|
||||
FILE* const file;
|
||||
bool const use_colors;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<Printer> Printer::create(FILE* out, bool use_colors) {
|
||||
return std::make_unique<PrinterLinux>(out, use_colors);
|
||||
}
|
||||
|
||||
} // namespace diag
|
||||
} // namespace tint
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2020 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/diagnostic/printer.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace tint {
|
||||
namespace diag {
|
||||
namespace {
|
||||
|
||||
class PrinterOther : public Printer {
|
||||
public:
|
||||
explicit PrinterOther(FILE* f) : file(f) {}
|
||||
|
||||
void write(const std::string& str, const Style&) override {
|
||||
fwrite(str.data(), 1, str.size(), file);
|
||||
}
|
||||
|
||||
private:
|
||||
FILE* file;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<Printer> Printer::create(FILE* out, bool) {
|
||||
return std::make_unique<PrinterOther>(out);
|
||||
}
|
||||
|
||||
} // namespace diag
|
||||
} // namespace tint
|
|
@ -0,0 +1,90 @@
|
|||
// Copyright 2020 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/diagnostic/printer.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace tint {
|
||||
namespace diag {
|
||||
namespace {
|
||||
|
||||
using PrinterTest = testing::Test;
|
||||
|
||||
// Actually verifying that the expected colors are printed is exceptionally
|
||||
// difficult as:
|
||||
// a) The color emission varies by OS.
|
||||
// b) The logic checks to see if the printer is writing to a terminal, making
|
||||
// mocking hard.
|
||||
// c) Actually probing what gets written to a FILE* is notoriously tricky.
|
||||
//
|
||||
// The least we can do is to exersice the code - which is what we do here.
|
||||
// The test will print each of the colors, and can be examined with human
|
||||
// eyeballs.
|
||||
|
||||
TEST_F(PrinterTest, WithColors) {
|
||||
auto printer = Printer::create(stdout, true);
|
||||
printer->write("Default", Style{Color::kDefault, false});
|
||||
printer->write("Black", Style{Color::kBlack, false});
|
||||
printer->write("Red", Style{Color::kRed, false});
|
||||
printer->write("Green", Style{Color::kGreen, false});
|
||||
printer->write("Yellow", Style{Color::kYellow, false});
|
||||
printer->write("Blue", Style{Color::kBlue, false});
|
||||
printer->write("Magenta", Style{Color::kMagenta, false});
|
||||
printer->write("Cyan", Style{Color::kCyan, false});
|
||||
printer->write("White", Style{Color::kWhite, false});
|
||||
}
|
||||
|
||||
TEST_F(PrinterTest, BoldWithColors) {
|
||||
auto printer = Printer::create(stdout, true);
|
||||
printer->write("Default", Style{Color::kDefault, true});
|
||||
printer->write("Black", Style{Color::kBlack, true});
|
||||
printer->write("Red", Style{Color::kRed, true});
|
||||
printer->write("Green", Style{Color::kGreen, true});
|
||||
printer->write("Yellow", Style{Color::kYellow, true});
|
||||
printer->write("Blue", Style{Color::kBlue, true});
|
||||
printer->write("Magenta", Style{Color::kMagenta, true});
|
||||
printer->write("Cyan", Style{Color::kCyan, true});
|
||||
printer->write("White", Style{Color::kWhite, true});
|
||||
}
|
||||
|
||||
TEST_F(PrinterTest, WithoutColors) {
|
||||
auto printer = Printer::create(stdout, false);
|
||||
printer->write("Default", Style{Color::kDefault, false});
|
||||
printer->write("Black", Style{Color::kBlack, false});
|
||||
printer->write("Red", Style{Color::kRed, false});
|
||||
printer->write("Green", Style{Color::kGreen, false});
|
||||
printer->write("Yellow", Style{Color::kYellow, false});
|
||||
printer->write("Blue", Style{Color::kBlue, false});
|
||||
printer->write("Magenta", Style{Color::kMagenta, false});
|
||||
printer->write("Cyan", Style{Color::kCyan, false});
|
||||
printer->write("White", Style{Color::kWhite, false});
|
||||
}
|
||||
|
||||
TEST_F(PrinterTest, BoldWithoutColors) {
|
||||
auto printer = Printer::create(stdout, false);
|
||||
printer->write("Default", Style{Color::kDefault, true});
|
||||
printer->write("Black", Style{Color::kBlack, true});
|
||||
printer->write("Red", Style{Color::kRed, true});
|
||||
printer->write("Green", Style{Color::kGreen, true});
|
||||
printer->write("Yellow", Style{Color::kYellow, true});
|
||||
printer->write("Blue", Style{Color::kBlue, true});
|
||||
printer->write("Magenta", Style{Color::kMagenta, true});
|
||||
printer->write("Cyan", Style{Color::kCyan, true});
|
||||
printer->write("White", Style{Color::kWhite, true});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace diag
|
||||
} // namespace tint
|
|
@ -0,0 +1,113 @@
|
|||
// Copyright 2020 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/diagnostic/printer.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#include <Windows.h>
|
||||
|
||||
namespace tint {
|
||||
namespace diag {
|
||||
namespace {
|
||||
|
||||
struct ConsoleInfo {
|
||||
HANDLE handle = INVALID_HANDLE_VALUE;
|
||||
WORD default_attributes = 0;
|
||||
operator bool() const { return handle != INVALID_HANDLE_VALUE; }
|
||||
};
|
||||
|
||||
ConsoleInfo console_info(FILE* file) {
|
||||
if (file == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
ConsoleInfo console{};
|
||||
if (file == stdout) {
|
||||
console.handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
} else if (file == stderr) {
|
||||
console.handle = GetStdHandle(STD_ERROR_HANDLE);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
|
||||
CONSOLE_SCREEN_BUFFER_INFO info{};
|
||||
if (GetConsoleScreenBufferInfo(console.handle, &info) == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
console.default_attributes = info.wAttributes;
|
||||
return console;
|
||||
}
|
||||
|
||||
class PrinterWindows : public Printer {
|
||||
public:
|
||||
PrinterWindows(FILE* f, bool use_colors)
|
||||
: file(f), console(console_info(use_colors ? f : nullptr)) {}
|
||||
|
||||
void write(const std::string& str, const Style& style) override {
|
||||
write_color(style.color, style.bold);
|
||||
fwrite(str.data(), 1, str.size(), file);
|
||||
write_color(Color::kDefault, false);
|
||||
}
|
||||
|
||||
private:
|
||||
WORD attributes(Color color, bool bold) {
|
||||
switch (color) {
|
||||
case Color::kDefault:
|
||||
return console.default_attributes;
|
||||
case Color::kBlack:
|
||||
return 0;
|
||||
case Color::kRed:
|
||||
return FOREGROUND_RED | (bold ? FOREGROUND_INTENSITY : 0);
|
||||
case Color::kGreen:
|
||||
return FOREGROUND_GREEN | (bold ? FOREGROUND_INTENSITY : 0);
|
||||
case Color::kYellow:
|
||||
return FOREGROUND_RED | FOREGROUND_GREEN |
|
||||
(bold ? FOREGROUND_INTENSITY : 0);
|
||||
case Color::kBlue:
|
||||
return FOREGROUND_BLUE | (bold ? FOREGROUND_INTENSITY : 0);
|
||||
case Color::kMagenta:
|
||||
return FOREGROUND_RED | FOREGROUND_BLUE |
|
||||
(bold ? FOREGROUND_INTENSITY : 0);
|
||||
case Color::kCyan:
|
||||
return FOREGROUND_GREEN | FOREGROUND_BLUE |
|
||||
(bold ? FOREGROUND_INTENSITY : 0);
|
||||
case Color::kWhite:
|
||||
return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
|
||||
(bold ? FOREGROUND_INTENSITY : 0);
|
||||
}
|
||||
return 0; // unreachable
|
||||
}
|
||||
|
||||
void write_color(Color color, bool bold) {
|
||||
if (console) {
|
||||
SetConsoleTextAttribute(console.handle, attributes(color, bold));
|
||||
fflush(file);
|
||||
}
|
||||
}
|
||||
|
||||
FILE* const file;
|
||||
ConsoleInfo const console;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<Printer> Printer::create(FILE* out, bool use_colors) {
|
||||
return std::make_unique<PrinterWindows>(out, use_colors);
|
||||
}
|
||||
|
||||
} // namespace diag
|
||||
} // namespace tint
|
Loading…
Reference in New Issue