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/diagnostic.h",
|
||||||
"src/diagnostic/formatter.cc",
|
"src/diagnostic/formatter.cc",
|
||||||
"src/diagnostic/formatter.h",
|
"src/diagnostic/formatter.h",
|
||||||
|
"src/diagnostic/printer.cc",
|
||||||
|
"src/diagnostic/printer.h",
|
||||||
"src/inspector/entry_point.cc",
|
"src/inspector/entry_point.cc",
|
||||||
"src/inspector/entry_point.h",
|
"src/inspector/entry_point.h",
|
||||||
"src/inspector/inspector.cc",
|
"src/inspector/inspector.cc",
|
||||||
|
@ -418,6 +420,14 @@ source_set("libtint_core_src") {
|
||||||
"src/writer/writer.h",
|
"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 = [
|
public_deps = [
|
||||||
":tint_core_enums_unified1",
|
":tint_core_enums_unified1",
|
||||||
":tint_core_tables_unified1",
|
":tint_core_tables_unified1",
|
||||||
|
@ -772,6 +782,7 @@ source_set("tint_unittests_core_src") {
|
||||||
"src/ast/variable_test.cc",
|
"src/ast/variable_test.cc",
|
||||||
"src/ast/workgroup_decoration_test.cc",
|
"src/ast/workgroup_decoration_test.cc",
|
||||||
"src/diagnostic/formatter_test.cc",
|
"src/diagnostic/formatter_test.cc",
|
||||||
|
"src/diagnostic/printer_test.cc",
|
||||||
"src/inspector/inspector_test.cc",
|
"src/inspector/inspector_test.cc",
|
||||||
"src/scope_stack_test.cc",
|
"src/scope_stack_test.cc",
|
||||||
"src/transform/bound_array_accessors_transform_test.cc",
|
"src/transform/bound_array_accessors_transform_test.cc",
|
||||||
|
|
|
@ -202,6 +202,8 @@ set(TINT_LIB_SRCS
|
||||||
diagnostic/diagnostic.h
|
diagnostic/diagnostic.h
|
||||||
diagnostic/formatter.cc
|
diagnostic/formatter.cc
|
||||||
diagnostic/formatter.h
|
diagnostic/formatter.h
|
||||||
|
diagnostic/printer.cc
|
||||||
|
diagnostic/printer.h
|
||||||
inspector/entry_point.cc
|
inspector/entry_point.cc
|
||||||
inspector/entry_point.h
|
inspector/entry_point.h
|
||||||
inspector/inspector.cc
|
inspector/inspector.cc
|
||||||
|
@ -239,6 +241,14 @@ set(TINT_LIB_SRCS
|
||||||
writer/writer.h
|
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})
|
if(${TINT_BUILD_SPV_READER})
|
||||||
list(APPEND TINT_LIB_SRCS
|
list(APPEND TINT_LIB_SRCS
|
||||||
reader/spirv/construct.h
|
reader/spirv/construct.h
|
||||||
|
@ -382,6 +392,7 @@ set(TINT_TEST_SRCS
|
||||||
ast/variable_test.cc
|
ast/variable_test.cc
|
||||||
ast/workgroup_decoration_test.cc
|
ast/workgroup_decoration_test.cc
|
||||||
diagnostic/formatter_test.cc
|
diagnostic/formatter_test.cc
|
||||||
|
diagnostic/printer_test.cc
|
||||||
inspector/inspector_test.cc
|
inspector/inspector_test.cc
|
||||||
scope_stack_test.cc
|
scope_stack_test.cc
|
||||||
transform/bound_array_accessors_transform_test.cc
|
transform/bound_array_accessors_transform_test.cc
|
||||||
|
|
|
@ -59,76 +59,137 @@ std::basic_ostream<CharT, Traits>& operator<<(
|
||||||
|
|
||||||
class BasicFormatter : public Formatter {
|
class BasicFormatter : public Formatter {
|
||||||
public:
|
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)
|
BasicFormatter(bool print_file, bool print_severity, bool print_line)
|
||||||
: print_file_(print_file),
|
: print_file_(print_file),
|
||||||
print_severity_(print_severity),
|
print_severity_(print_severity),
|
||||||
print_line_(print_line) {}
|
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;
|
bool first = true;
|
||||||
std::stringstream ss;
|
|
||||||
for (auto diag : list) {
|
for (auto diag : list) {
|
||||||
|
state.set_style({});
|
||||||
if (!first) {
|
if (!first) {
|
||||||
ss << std::endl;
|
state.newline();
|
||||||
}
|
}
|
||||||
format(diag, ss);
|
format(diag, &state);
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
return ss.str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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& src = diag.source;
|
||||||
auto const& rng = src.range;
|
auto const& rng = src.range;
|
||||||
|
|
||||||
|
state->set_style({Color::kDefault, true});
|
||||||
|
|
||||||
if (print_file_ && src.file != nullptr && !src.file->path.empty()) {
|
if (print_file_ && src.file != nullptr && !src.file->path.empty()) {
|
||||||
ss << src.file->path;
|
(*state) << src.file->path;
|
||||||
if (rng.begin.line > 0) {
|
if (rng.begin.line > 0) {
|
||||||
ss << ":" << rng.begin;
|
(*state) << ":" << rng.begin;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ss << rng.begin;
|
(*state) << rng.begin;
|
||||||
}
|
}
|
||||||
if (print_severity_) {
|
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;
|
||||||
}
|
}
|
||||||
ss << ": " << diag.message;
|
(*state) << " " << diag.severity;
|
||||||
|
}
|
||||||
|
|
||||||
|
state->set_style({Color::kDefault, true});
|
||||||
|
(*state) << ": " << diag.message;
|
||||||
|
|
||||||
if (print_line_ && src.file != nullptr && rng.begin.line > 0) {
|
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++) {
|
for (size_t line = rng.begin.line; line <= rng.end.line; line++) {
|
||||||
if (line < src.file->lines.size() + 1) {
|
if (line < src.file->lines.size() + 1) {
|
||||||
auto len = src.file->lines[line - 1].size();
|
auto len = src.file->lines[line - 1].size();
|
||||||
|
|
||||||
ss << src.file->lines[line - 1];
|
(*state) << src.file->lines[line - 1];
|
||||||
ss << std::endl;
|
state->newline();
|
||||||
|
state->set_style({Color::kCyan, false});
|
||||||
|
|
||||||
if (line == rng.begin.line && line == rng.end.line) {
|
if (line == rng.begin.line && line == rng.end.line) {
|
||||||
// Single line
|
// Single line
|
||||||
repeat(' ', rng.begin.column - 1, ss);
|
state->repeat(' ', rng.begin.column - 1);
|
||||||
repeat('^', std::max<size_t>(rng.end.column - rng.begin.column, 1),
|
state->repeat(
|
||||||
ss);
|
'^', std::max<size_t>(rng.end.column - rng.begin.column, 1));
|
||||||
} else if (line == rng.begin.line) {
|
} else if (line == rng.begin.line) {
|
||||||
// Start of multi-line
|
// Start of multi-line
|
||||||
repeat(' ', rng.begin.column - 1, ss);
|
state->repeat(' ', rng.begin.column - 1);
|
||||||
repeat('^', len - (rng.begin.column - 1), ss);
|
state->repeat('^', len - (rng.begin.column - 1));
|
||||||
} else if (line == rng.end.line) {
|
} else if (line == rng.end.line) {
|
||||||
// End of multi-line
|
// End of multi-line
|
||||||
repeat('^', rng.end.column - 1, ss);
|
state->repeat('^', rng.end.column - 1);
|
||||||
} else {
|
} else {
|
||||||
// Middle of multi-line
|
// 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 {
|
state->set_style({});
|
||||||
while (n-- > 0) {
|
|
||||||
ss << c;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "src/diagnostic/printer.h"
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
namespace diag {
|
namespace diag {
|
||||||
|
|
||||||
|
@ -37,8 +39,18 @@ class Formatter {
|
||||||
|
|
||||||
virtual ~Formatter();
|
virtual ~Formatter();
|
||||||
|
|
||||||
/// @return the human readable list of diagnostics formatted to a string.
|
/// format prints the formatted diagnostic list |list| to |printer|.
|
||||||
virtual std::string format(const List&) const = 0;
|
/// @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
|
} // 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