diff --git a/binding.gyp b/binding.gyp index 8da0c457..3165af76 100644 --- a/binding.gyp +++ b/binding.gyp @@ -79,6 +79,7 @@ "src/llscan.cc", "src/node.cc", "src/node-constants.cc", + "src/settings.cc", ], "conditions": [ [ "OS == 'win'", { @@ -97,7 +98,7 @@ "include_dirs": [ " diff --git a/deps/rang/README.md b/deps/rang/README.md new file mode 100644 index 00000000..90a196a2 --- /dev/null +++ b/deps/rang/README.md @@ -0,0 +1,159 @@ +# rang [![Build Status](https://travis-ci.org/agauniyal/rang.svg?branch=master)](https://travis-ci.org/agauniyal/rang) [![Build status](https://ci.appveyor.com/api/projects/status/jqpdoelli38h2a7w?svg=true)](https://ci.appveyor.com/project/agauniyal/rang) [![codecov](https://codecov.io/gh/agauniyal/rang/branch/master/graph/badge.svg)](https://codecov.io/gh/agauniyal/rang) [ ![Download](https://api.bintray.com/packages/agauniyal/rang/rang%3Arang/images/download.svg) ](https://bintray.com/agauniyal/rang/rang%3Arang/_latestVersion) + +##### Colors for your Terminal. + +![rang-demo](https://cloud.githubusercontent.com/assets/7630575/13501282/0bd00074-e18c-11e5-9848-5bd1f20566d9.gif) +
+ Windows Demo + +![rang-windows-demo](https://cloud.githubusercontent.com/assets/11349690/19836886/8134975e-9ebe-11e6-9ee4-c4657784ff3b.gif) +
+ + +Example usage +------------- + +```c++ +#include "rang.hpp" + +using namespace std; +using namespace rang; + +int main() +{ + cout << "Plain old text" + << style::bold << "Rang styled text!!" + << style::reset << endl; +} +``` + +Dependencies +------------ +*rang* only depends on [C++ standard library](http://en.cppreference.com/w/cpp/header), `unistd.h` system header on unix and `windows.h` & `io.h` system headers on windows based systems. In other words, you don't need any 3rd party dependencies. + + +Installation +------------ + +*rang* is a single header-only library. Put `rang.hpp` in the [include](include) folder directly into the project source tree or somewhere reachable from your project. + +Or, if you use the [conan package manager](https://www.conan.io/), follow these steps: + +1. Add a reference to *rang* to the *requires* section of your project's `conanfile.txt` file: + + [requires] + rang/3.1.0@rang/stable + +2. Run conan's install command: + + conan install + + +## How to use + +*Rang* uses iostream objects - `cout`/`clog`/`cerr` to apply attributes to output text. Since *rang* aims to support both windows and unix like systems, it takes care of the os specific details and tries to provide a uniform interface. Due to incompatiblities b/w different OS versions, not all kinds of attributes are supported on every system so rang will try to skip the ones which might produce garbage(instead of pushing random ANSI escape codes on your streams). Detection of tty is also handled internally so you don't need to check if application user might redirect output to a file. + +> **Need support for non-ansi terminals? Check out [Termdb](https://github.com/agauniyal/termdb) which supports virtually all terminals and their capablities.** + +Apart from setting text attributes, you can also ask rang to override its default behaviour through these methods - +```cpp +void rang::setControlMode(rang::control); +``` +where `rang::control` takes + - `control::Auto` - Automatically detects whether terminal supports color or not(**Default**) + - `control::Off` - Turn off colors completely + - `control::Force` - Force colors even if terminal doesn't supports them or output is redirected to non-terminal + +```cpp +void rang::setWinTermMode(rang::winTerm); +``` +where `rang::winTerm` takes + - `winTerm::Auto` - Checks for newer windows and picks Ansi otherwise falls back to Native(**Default**) + - `winTerm::Native` - This method is supported in all versions of windows but supports less attributes + - `winTerm::Ansi` - This method is supported in newer versions of windows and supports rich variety of attributes + + +Supported attributes with their compatiblity are listed below - + +**Text Styles**: + +| Code | Linux/Win/Others | Old Win +| ---- | --------- | ------ | +| `rang::style::bold` | yes | yes | +| `rang::style::dim` | yes | no | +| `rang::style::italic` | yes | no | +| `rang::style::underline` | yes | no | +| `rang::style::blink` | no | no | +| `rang::style::rblink` | no | no | +| `rang::style::reversed` | yes | yes | +| `rang::style::conceal` | maybe | yes | +| `rang::style::crossed` | yes | no | + +**Text Color**: + +| Code | Linux/Win/Others | Old Win +| ---- | --------- | ------ | +| `rang::fg::black` | yes | yes | +| `rang::fg::red` | yes | yes | +| `rang::fg::green` | yes | yes | +| `rang::fg::yellow` | yes | yes | +| `rang::fg::blue` | yes | yes | +| `rang::fg::magenta` | yes | yes | +| `rang::fg::cyan` | yes | yes | +| `rang::fg::gray` | yes | yes | + +**Background Color**: + +| Code | Linux/Win/Others | Old Win +| ---- | --------- | ------ | +| `rang::bg::black` | yes | yes | +| `rang::bg::red` | yes | yes | +| `rang::bg::green` | yes | yes | +| `rang::bg::yellow` | yes | yes | +| `rang::bg::blue` | yes | yes | +| `rang::bg::magenta` | yes | yes | +| `rang::bg::cyan` | yes | yes | +| `rang::bg::gray` | yes | yes | + +**Bright Foreground Color**: + +| Code | Linux/Win/Others | Old Win +| ---- | --------- | ------ | +| `rang::fgB::black` | yes | yes | +| `rang::fgB::red` | yes | yes | +| `rang::fgB::green` | yes | yes | +| `rang::fgB::yellow` | yes | yes | +| `rang::fgB::blue` | yes | yes | +| `rang::fgB::magenta` | yes | yes | +| `rang::fgB::cyan` | yes | yes | +| `rang::fgB::gray` | yes | yes | + +**Bright Background Color**: + +| Code | Linux/Win/Others | Old Win +| ---- | --------- | ------ | +| `rang::bgB::black` | yes | yes | +| `rang::bgB::red` | yes | yes | +| `rang::bgB::green` | yes | yes | +| `rang::bgB::yellow` | yes | yes | +| `rang::bgB::blue` | yes | yes | +| `rang::bgB::magenta` | yes | yes | +| `rang::bgB::cyan` | yes | yes | +| `rang::bgB::gray` | yes | yes | + +**Reset Styles/Colors**: + +| Code | Linux/Win/Others | Old Win +| ---- | --------- | ------ | +| `rang::style::reset` | yes | yes | +| `rang::fg::reset` | yes | yes | +| `rang::bg::reset` | yes | yes | + +----- +## My terminal is not detected/gets garbage output! + +Check your env variable `TERM`'s value. Then open an issue [here](https://github.com/agauniyal/rang/issues/new) and make sure to mention `TERM`'s value along with your terminal name. + +## Redirecting `cout`/`cerr`/`clog` rdbuf? + +Rang doesn't interfere if you try to redirect `cout`/`cerr`/`clog` to somewhere else and leaves the decision to the library user. Make sure you've read this [conversation](https://github.com/agauniyal/rang/pull/77#issuecomment-360991652) and check out the example code [here](https://gist.github.com/kingseva/a918ec66079a9475f19642ec31276a21). diff --git a/deps/rang/include/rang.hpp b/deps/rang/include/rang.hpp new file mode 100644 index 00000000..52415f20 --- /dev/null +++ b/deps/rang/include/rang.hpp @@ -0,0 +1,483 @@ +#ifndef RANG_DOT_HPP +#define RANG_DOT_HPP + +#if defined(__unix__) || defined(__unix) || defined(__linux__) +#define OS_LINUX +#elif defined(WIN32) || defined(_WIN32) || defined(_WIN64) +#define OS_WIN +#elif defined(__APPLE__) || defined(__MACH__) +#define OS_MAC +#else +#error Unknown Platform +#endif + +#if defined(OS_LINUX) || defined(OS_MAC) +#include + +#elif defined(OS_WIN) + +#if defined(_WIN32_WINNT) && (_WIN32_WINNT < 0x0600) +#error \ + "Please include rang.hpp before any windows system headers or set _WIN32_WINNT at least to _WIN32_WINNT_VISTA" +#elif !defined(_WIN32_WINNT) +#define _WIN32_WINNT _WIN32_WINNT_VISTA +#endif + +#include +#include +#include + +// Only defined in windows 10 onwards, redefining in lower windows since it +// doesn't gets used in lower versions +// https://docs.microsoft.com/en-us/windows/console/getconsolemode +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 +#endif + +#endif + +#include +#include +#include +#include +#include + +namespace rang { + +/* For better compability with most of terminals do not use any style settings + * except of reset, bold and reversed. + * Note that on Windows terminals bold style is same as fgB color. + */ +enum class style { + reset = 0, + bold = 1, + dim = 2, + italic = 3, + underline = 4, + blink = 5, + rblink = 6, + reversed = 7, + conceal = 8, + crossed = 9 +}; + +enum class fg { + black = 30, + red = 31, + green = 32, + yellow = 33, + blue = 34, + magenta = 35, + cyan = 36, + gray = 37, + reset = 39 +}; + +enum class bg { + black = 40, + red = 41, + green = 42, + yellow = 43, + blue = 44, + magenta = 45, + cyan = 46, + gray = 47, + reset = 49 +}; + +enum class fgB { + black = 90, + red = 91, + green = 92, + yellow = 93, + blue = 94, + magenta = 95, + cyan = 96, + gray = 97 +}; + +enum class bgB { + black = 100, + red = 101, + green = 102, + yellow = 103, + blue = 104, + magenta = 105, + cyan = 106, + gray = 107 +}; + +enum class control { // Behaviour of rang function calls + Off = 0, // toggle off rang style/color calls + Auto = 1, // (Default) autodect terminal and colorize if needed + Force = 2 // force ansi color output to non terminal streams +}; +// Use rang::setControlMode to set rang control mode + +enum class winTerm { // Windows Terminal Mode + Auto = 0, // (Default) automatically detects wheter Ansi or Native API + Ansi = 1, // Force use Ansi API + Native = 2 // Force use Native API +}; +// Use rang::setWinTermMode to explicitly set terminal API for Windows +// Calling rang::setWinTermMode have no effect on other OS + +namespace rang_implementation { + +inline std::atomic &controlMode() noexcept { + static std::atomic value(control::Auto); + return value; +} + +inline std::atomic &winTermMode() noexcept { + static std::atomic termMode(winTerm::Auto); + return termMode; +} + +inline bool supportsColor() noexcept { +#if defined(OS_LINUX) || defined(OS_MAC) + + static const bool result = [] { + const char *Terms[] = {"ansi", "color", "console", "cygwin", "gnome", + "konsole", "kterm", "linux", "msys", "putty", + "rxvt", "screen", "vt100", "xterm"}; + + const char *env_p = std::getenv("TERM"); + if (env_p == nullptr) { + return false; + } + return std::any_of( + std::begin(Terms), std::end(Terms), + [&](const char *term) { return std::strstr(env_p, term) != nullptr; }); + }(); + +#elif defined(OS_WIN) + // All windows versions support colors through native console methods + static constexpr bool result = true; +#endif + return result; +} + +#ifdef OS_WIN + + +inline bool isMsysPty(int fd) noexcept { + // Dynamic load for binary compability with old Windows + const auto ptrGetFileInformationByHandleEx = + reinterpret_cast( + GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), + "GetFileInformationByHandleEx")); + if (!ptrGetFileInformationByHandleEx) { + return false; + } + + HANDLE h = reinterpret_cast(_get_osfhandle(fd)); + if (h == INVALID_HANDLE_VALUE) { + return false; + } + + // Check that it's a pipe: + if (GetFileType(h) != FILE_TYPE_PIPE) { + return false; + } + + // POD type is binary compatible with FILE_NAME_INFO from WinBase.h + // It have the same alignment and used to avoid UB in caller code + struct MY_FILE_NAME_INFO { + DWORD FileNameLength; + WCHAR FileName[MAX_PATH]; + }; + + auto pNameInfo = std::unique_ptr(new (std::nothrow) + MY_FILE_NAME_INFO()); + if (!pNameInfo) { + return false; + } + + // Check pipe name is template of + // {"cygwin-","msys-"}XXXXXXXXXXXXXXX-ptyX-XX + if (!ptrGetFileInformationByHandleEx(h, FileNameInfo, pNameInfo.get(), + sizeof(MY_FILE_NAME_INFO))) { + return false; + } + std::wstring name(pNameInfo->FileName, + pNameInfo->FileNameLength / sizeof(WCHAR)); + if ((name.find(L"msys-") == std::wstring::npos && + name.find(L"cygwin-") == std::wstring::npos) || + name.find(L"-pty") == std::wstring::npos) { + return false; + } + + return true; +} + +#endif + +inline bool isTerminal(const std::streambuf *osbuf) noexcept { + using std::cerr; + using std::clog; + using std::cout; +#if defined(OS_LINUX) || defined(OS_MAC) + if (osbuf == cout.rdbuf()) { + static const bool cout_term = isatty(fileno(stdout)) != 0; + return cout_term; + } else if (osbuf == cerr.rdbuf() || osbuf == clog.rdbuf()) { + static const bool cerr_term = isatty(fileno(stderr)) != 0; + return cerr_term; + } +#elif defined(OS_WIN) + if (osbuf == cout.rdbuf()) { + static const bool cout_term = + (_isatty(_fileno(stdout)) || isMsysPty(_fileno(stdout))); + return cout_term; + } else if (osbuf == cerr.rdbuf() || osbuf == clog.rdbuf()) { + static const bool cerr_term = + (_isatty(_fileno(stderr)) || isMsysPty(_fileno(stderr))); + return cerr_term; + } +#endif + return false; +} + +template +using enableStd = typename std::enable_if< + std::is_same::value || std::is_same::value || + std::is_same::value || std::is_same::value || + std::is_same::value, + std::ostream &>::type; + + +#ifdef OS_WIN + +struct SGR { // Select Graphic Rendition parameters for Windows console + BYTE fgColor; // foreground color (0-15) lower 3 rgb bits + intense bit + BYTE bgColor; // background color (0-15) lower 3 rgb bits + intense bit + BYTE bold; // emulated as FOREGROUND_INTENSITY bit + BYTE underline; // emulated as BACKGROUND_INTENSITY bit + BOOLEAN inverse; // swap foreground/bold & background/underline + BOOLEAN conceal; // set foreground/bold to background/underline +}; + +enum class AttrColor : BYTE { // Color attributes for console screen buffer + black = 0, + red = 4, + green = 2, + yellow = 6, + blue = 1, + magenta = 5, + cyan = 3, + gray = 7 +}; + +inline HANDLE getConsoleHandle(const std::streambuf *osbuf) noexcept { + if (osbuf == std::cout.rdbuf()) { + static const HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); + return hStdout; + } else if (osbuf == std::cerr.rdbuf() || osbuf == std::clog.rdbuf()) { + static const HANDLE hStderr = GetStdHandle(STD_ERROR_HANDLE); + return hStderr; + } + return INVALID_HANDLE_VALUE; +} + +inline bool setWinTermAnsiColors(const std::streambuf *osbuf) noexcept { + HANDLE h = getConsoleHandle(osbuf); + if (h == INVALID_HANDLE_VALUE) { + return false; + } + DWORD dwMode = 0; + if (!GetConsoleMode(h, &dwMode)) { + return false; + } + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if (!SetConsoleMode(h, dwMode)) { + return false; + } + return true; +} + +inline bool supportsAnsi(const std::streambuf *osbuf) noexcept { + using std::cerr; + using std::clog; + using std::cout; + if (osbuf == cout.rdbuf()) { + static const bool cout_ansi = + (isMsysPty(_fileno(stdout)) || setWinTermAnsiColors(osbuf)); + return cout_ansi; + } else if (osbuf == cerr.rdbuf() || osbuf == clog.rdbuf()) { + static const bool cerr_ansi = + (isMsysPty(_fileno(stderr)) || setWinTermAnsiColors(osbuf)); + return cerr_ansi; + } + return false; +} + +inline const SGR &defaultState() noexcept { + static const SGR defaultSgr = []() -> SGR { + CONSOLE_SCREEN_BUFFER_INFO info; + WORD attrib = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info) || + GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &info)) { + attrib = info.wAttributes; + } + SGR sgr = {0, 0, 0, 0, FALSE, FALSE}; + sgr.fgColor = attrib & 0x0F; + sgr.bgColor = (attrib & 0xF0) >> 4; + return sgr; + }(); + return defaultSgr; +} + +inline BYTE ansi2attr(BYTE rgb) noexcept { + static const AttrColor rev[8] = { + AttrColor::black, AttrColor::red, AttrColor::green, AttrColor::yellow, + AttrColor::blue, AttrColor::magenta, AttrColor::cyan, AttrColor::gray}; + return static_cast(rev[rgb]); +} + +inline void setWinSGR(rang::bg col, SGR &state) noexcept { + if (col != rang::bg::reset) { + state.bgColor = ansi2attr(static_cast(col) - 40); + } else { + state.bgColor = defaultState().bgColor; + } +} + +inline void setWinSGR(rang::fg col, SGR &state) noexcept { + if (col != rang::fg::reset) { + state.fgColor = ansi2attr(static_cast(col) - 30); + } else { + state.fgColor = defaultState().fgColor; + } +} + +inline void setWinSGR(rang::bgB col, SGR &state) noexcept { + state.bgColor = + (BACKGROUND_INTENSITY >> 4) | ansi2attr(static_cast(col) - 100); +} + +inline void setWinSGR(rang::fgB col, SGR &state) noexcept { + state.fgColor = FOREGROUND_INTENSITY | ansi2attr(static_cast(col) - 90); +} + +inline void setWinSGR(rang::style style, SGR &state) noexcept { + switch (style) { + case rang::style::reset: + state = defaultState(); + break; + case rang::style::bold: + state.bold = FOREGROUND_INTENSITY; + break; + case rang::style::underline: + case rang::style::blink: + state.underline = BACKGROUND_INTENSITY; + break; + case rang::style::reversed: + state.inverse = TRUE; + break; + case rang::style::conceal: + state.conceal = TRUE; + break; + default: + break; + } +} + +inline SGR ¤t_state() noexcept { + static SGR state = defaultState(); + return state; +} + +inline WORD SGR2Attr(const SGR &state) noexcept { + WORD attrib = 0; + if (state.conceal) { + if (state.inverse) { + attrib = (state.fgColor << 4) | state.fgColor; + if (state.bold) attrib |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY; + } else { + attrib = (state.bgColor << 4) | state.bgColor; + if (state.underline) + attrib |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY; + } + } else if (state.inverse) { + attrib = (state.fgColor << 4) | state.bgColor; + if (state.bold) attrib |= BACKGROUND_INTENSITY; + if (state.underline) attrib |= FOREGROUND_INTENSITY; + } else { + attrib = + state.fgColor | (state.bgColor << 4) | state.bold | state.underline; + } + return attrib; +} + +template +inline void setWinColorAnsi(std::ostream &os, T const value) { + os << "\033[" << static_cast(value) << "m"; +} + +template +inline void setWinColorNative(std::ostream &os, T const value) { + const HANDLE h = getConsoleHandle(os.rdbuf()); + if (h != INVALID_HANDLE_VALUE) { + setWinSGR(value, current_state()); + // Out all buffered text to console with previous settings: + os.flush(); + SetConsoleTextAttribute(h, SGR2Attr(current_state())); + } +} + +template +inline enableStd setColor(std::ostream &os, T const value) { + if (winTermMode() == winTerm::Auto) { + if (supportsAnsi(os.rdbuf())) { + setWinColorAnsi(os, value); + } else { + setWinColorNative(os, value); + } + } else if (winTermMode() == winTerm::Ansi) { + setWinColorAnsi(os, value); + } else { + setWinColorNative(os, value); + } + return os; +} +#else +template +inline enableStd setColor(std::ostream &os, T const value) { + return os << "\033[" << static_cast(value) << "m"; +} +#endif +} // namespace rang_implementation + +template +inline rang_implementation::enableStd operator<<(std::ostream &os, + const T value) { + const control option = rang_implementation::controlMode(); + switch (option) { + case control::Auto: + return rang_implementation::supportsColor() && + rang_implementation::isTerminal(os.rdbuf()) + ? rang_implementation::setColor(os, value) + : os; + case control::Force: + return rang_implementation::setColor(os, value); + default: + return os; + } +} + +inline void setWinTermMode(const rang::winTerm value) noexcept { + rang_implementation::winTermMode() = value; +} + +inline void setControlMode(const control value) noexcept { + rang_implementation::controlMode() = value; +} + +} // namespace rang + +#undef OS_LINUX +#undef OS_WIN +#undef OS_MAC + +#endif /* ifndef RANG_DOT_HPP */ diff --git a/src/llnode.cc b/src/llnode.cc index 31722844..d1d8d8e9 100644 --- a/src/llnode.cc +++ b/src/llnode.cc @@ -9,11 +9,13 @@ #include +#include "deps/rang/include/rang.hpp" #include "src/error.h" #include "src/llnode.h" #include "src/llscan.h" #include "src/llv8.h" #include "src/node-inl.h" +#include "src/settings.h" namespace llnode { @@ -104,6 +106,35 @@ bool BacktraceCmd::DoExecute(SBDebugger d, char** cmd, return true; } +bool SetPropertyColorCmd::DoExecute(SBDebugger d, char** cmd, + SBCommandReturnObject& result) { +#ifdef NO_COLOR_OUTPUT + result.Printf("Color support is not available\n"); + return false; +#endif + if (cmd != nullptr) { + Settings* settings = Settings::GetSettings(); + char* arg = cmd[0]; + if (strcmp(arg, "always") == 0) { + settings->SetColor(arg); + result.Printf("Color set to 'always'\n"); + return true; + } + if (strcmp(arg, "never") == 0) { + settings->SetColor(arg); + result.Printf("Color set to 'never'\n"); + return true; + } + if (strcmp(arg, "auto") == 0) { + settings->SetColor(arg); + result.Printf("Color set to 'auto'\n"); + return true; + } + } + result.Printf("Error: Available options are (always | never | auto)\n"); + return false; +} + bool PrintCmd::DoExecute(SBDebugger d, char** cmd, SBCommandReturnObject& result) { @@ -418,6 +449,15 @@ bool PluginInitialize(SBDebugger d) { #endif // LLDB_SBMemoryRegionInfoList_h_ ); + SBCommand settingsCmd = + v8.AddMultiwordCommand("settings", "Interpreter settings"); + + SBCommand setPropertyCmd = + settingsCmd.AddMultiwordCommand("set", "Set a property"); + + setPropertyCmd.AddCommand("color", new llnode::SetPropertyColorCmd(&llv8), + "Set color property value"); + interpreter.AddCommand("findjsobjects", new llnode::FindObjectsCmd(&llscan), "Alias for `v8 findjsobjects`"); @@ -457,6 +497,11 @@ bool PluginInitialize(SBDebugger d) { "Print all pending requests in the queue. Equivalent to " "running process._getActiveRequests() on the living process.\n"); + // Set initial value for color support + llnode::Settings* settings = llnode::Settings::GetSettings(); + settings->SetColor("auto"); + + return true; } diff --git a/src/llnode.h b/src/llnode.h index 539cd66a..6366365c 100644 --- a/src/llnode.h +++ b/src/llnode.h @@ -24,6 +24,18 @@ class BacktraceCmd : public CommandBase { v8::LLV8* llv8_; }; +class SetPropertyColorCmd : public CommandBase { + public: + SetPropertyColorCmd(v8::LLV8* llv8) : llv8_(llv8) {} + ~SetPropertyColorCmd() override {} + + bool DoExecute(lldb::SBDebugger d, char** cmd, + lldb::SBCommandReturnObject& result) override; + + private: + v8::LLV8* llv8_; +}; + class PrintCmd : public CommandBase { public: PrintCmd(v8::LLV8* llv8, bool detailed) : llv8_(llv8), detailed_(detailed) {} diff --git a/src/llscan.cc b/src/llscan.cc index f1241ce7..7cc561ad 100644 --- a/src/llscan.cc +++ b/src/llscan.cc @@ -6,14 +6,18 @@ #include #include #include +#include +#include +#include #include #include +#include "deps/rang/include/rang.hpp" #include "src/error.h" -#include "src/llnode.h" #include "src/llscan.h" #include "src/llv8-inl.h" +#include "src/settings.h" namespace llnode { @@ -28,18 +32,6 @@ using lldb::SBValue; using lldb::eReturnStatusFailed; using lldb::eReturnStatusSuccessFinishResult; -const char* const - FindReferencesCmd::ObjectScanner::property_reference_template = - "0x%" PRIx64 ": %s.%s=0x%" PRIx64 "\n"; -const char* const FindReferencesCmd::ObjectScanner::array_reference_template = - "0x%" PRIx64 ": %s[%" PRId64 "]=0x%" PRIx64 "\n"; - - -const char* const - FindReferencesCmd::StringScanner::property_reference_template = - "0x%" PRIx64 ": %s.%s=0x%" PRIx64 " '%s'\n"; -const char* const FindReferencesCmd::StringScanner::array_reference_template = - "0x%" PRIx64 ": %s[%" PRId64 "]=0x%" PRIx64 " '%s'\n"; char** ParseInspectOptions(char** cmd, v8::Value::InspectOptions* options) { static struct option opts[] = { @@ -241,7 +233,13 @@ bool FindInstancesCmd::DoExecute(SBDebugger d, char** cmd, } } else { - result.Printf("No objects found with type name %s\n", type_name.c_str()); + // "No objects found with type name %s", type_name + std::stringstream ss; + ss << rang::style::bold << rang::fg::red + << "No objects found with type name " << type_name << rang::fg::reset + << rang::style::reset << std::endl; + std::string str(ss.str()); + result.Printf("%s", str.c_str()); result.SetStatus(eReturnStatusFailed); return false; } @@ -706,6 +704,25 @@ void FindReferencesCmd::ReferenceScanner::PrintContextRefs( } } +std::string FindReferencesCmd::ObjectScanner::GetPropertyReferenceString() { + std::stringstream ss; + ss << rang::fg::cyan << "0x%" PRIx64 << rang::fg::reset << ": " + << rang::fg::magenta << "%s" << rang::style::bold << rang::fg::yellow + << ".%s" << rang::fg::reset << rang::style::reset << "=" << rang::fg::cyan + << "0x%" PRIx64 << rang::fg::reset << "\n"; + return ss.str(); +} + +std::string FindReferencesCmd::ObjectScanner::GetArrayReferenceString() { + std::stringstream ss; + ss << rang::fg::cyan << "0x%" PRIx64 << rang::fg::reset << ": " + << rang::fg::magenta << "%s" << rang::style::bold << rang::fg::yellow + << "[%" PRId64 "]" << rang::fg::reset << rang::style::reset << "=" + << rang::fg::cyan << "0x%" PRIx64 << rang::fg::reset << "\n"; + return ss.str(); +} + + void FindReferencesCmd::ReferenceScanner::PrintRefs( SBCommandReturnObject& result, v8::JSObject& js_obj, Error& err) { int64_t length = js_obj.GetArrayLength(err); @@ -718,8 +735,10 @@ void FindReferencesCmd::ReferenceScanner::PrintRefs( if (v.raw() != search_value_.raw()) continue; std::string type_name = js_obj.GetTypeName(err); - result.Printf(array_reference_template, js_obj.raw(), type_name.c_str(), i, - search_value_.raw()); + + std::string reference_template(GetArrayReferenceString()); + result.Printf(reference_template.c_str(), js_obj.raw(), type_name.c_str(), + i, search_value_.raw()); } // Walk all the properties in this object. @@ -734,8 +753,10 @@ void FindReferencesCmd::ReferenceScanner::PrintRefs( if (v.raw() == search_value_.raw()) { std::string key = entry.first.ToString(err); std::string type_name = js_obj.GetTypeName(err); - result.Printf(property_reference_template, js_obj.raw(), - type_name.c_str(), key.c_str(), search_value_.raw()); + + std::string reference_template(GetPropertyReferenceString()); + result.Printf(reference_template.c_str(), js_obj.raw(), type_name.c_str(), + key.c_str(), search_value_.raw()); } } } @@ -754,7 +775,9 @@ void FindReferencesCmd::ReferenceScanner::PrintRefs( v8::String parent = sliced_str.Parent(err); if (err.Success() && parent.raw() == search_value_.raw()) { std::string type_name = sliced_str.GetTypeName(err); - result.Printf(property_reference_template, str.raw(), type_name.c_str(), + + std::string reference_template(GetPropertyReferenceString()); + result.Printf(reference_template.c_str(), str.raw(), type_name.c_str(), "", search_value_.raw()); } } else if (repr == v8->string()->kConsStringTag) { @@ -763,14 +786,18 @@ void FindReferencesCmd::ReferenceScanner::PrintRefs( v8::String first = cons_str.First(err); if (err.Success() && first.raw() == search_value_.raw()) { std::string type_name = cons_str.GetTypeName(err); - result.Printf(property_reference_template, str.raw(), type_name.c_str(), + + std::string reference_template(GetPropertyReferenceString()); + result.Printf(reference_template.c_str(), str.raw(), type_name.c_str(), "", search_value_.raw()); } v8::String second = cons_str.Second(err); if (err.Success() && second.raw() == search_value_.raw()) { std::string type_name = cons_str.GetTypeName(err); - result.Printf(property_reference_template, str.raw(), type_name.c_str(), + + std::string reference_template(GetPropertyReferenceString()); + result.Printf(reference_template.c_str(), str.raw(), type_name.c_str(), "", search_value_.raw()); } } else if (repr == v8->string()->kThinStringTag) { @@ -778,7 +805,9 @@ void FindReferencesCmd::ReferenceScanner::PrintRefs( v8::String actual = thin_str.Actual(err); if (err.Success() && actual.raw() == search_value_.raw()) { std::string type_name = thin_str.GetTypeName(err); - result.Printf(property_reference_template, str.raw(), type_name.c_str(), + + std::string reference_template(GetPropertyReferenceString()); + result.Printf(reference_template.c_str(), str.raw(), type_name.c_str(), "", search_value_.raw()); } } @@ -900,8 +929,10 @@ void FindReferencesCmd::PropertyScanner::PrintRefs( } if (key == search_value_) { std::string type_name = js_obj.GetTypeName(err); - result.Printf(property_reference_template, js_obj.raw(), - type_name.c_str(), key.c_str(), entry.second.raw()); + + std::string reference_template(GetPropertyReferenceString()); + result.Printf(reference_template.c_str(), js_obj.raw(), type_name.c_str(), + key.c_str(), entry.second.raw()); } } } @@ -967,8 +998,12 @@ void FindReferencesCmd::StringScanner::PrintRefs(SBCommandReturnObject& result, if (err.Success() && search_value_ == value) { std::string type_name = js_obj.GetTypeName(err); - result.Printf("0x%" PRIx64 ": %s[%" PRId64 "]=0x%" PRIx64 " '%s'\n", - js_obj.raw(), type_name.c_str(), i, v.raw(), + std::stringstream ss; + ss << rang::fg::cyan << std::hex << js_obj.raw() << std::dec + << rang::fg::reset; + + result.Printf("%s: %s[%" PRId64 "]=0x%" PRIx64 " '%s'\n", + ss.str().c_str(), type_name.c_str(), i, v.raw(), value.c_str()); } } @@ -997,9 +1032,14 @@ void FindReferencesCmd::StringScanner::PrintRefs(SBCommandReturnObject& result, continue; } std::string type_name = js_obj.GetTypeName(err); - result.Printf("0x%" PRIx64 ": %s.%s=0x%" PRIx64 " '%s'\n", - js_obj.raw(), type_name.c_str(), key.c_str(), - entry.second.raw(), value.c_str()); + + std::stringstream ss; + ss << rang::fg::cyan << "0x" << std::hex << js_obj.raw() + << rang::fg::reset << std::dec << ": " << type_name.c_str() << "." + << key.c_str() << "=" << std::hex << entry.second.raw() << std::dec + << " '" << value.c_str() << "'" << std::endl; + + result.Printf("%s", ss.str().c_str()); } } } diff --git a/src/llscan.h b/src/llscan.h index 419fbd37..9732debf 100644 --- a/src/llscan.h +++ b/src/llscan.h @@ -93,8 +93,8 @@ class FindReferencesCmd : public CommandBase { virtual void PrintContextRefs(lldb::SBCommandReturnObject& result, Error& err) {} - static const char* const property_reference_template; - static const char* const array_reference_template; + std::string GetPropertyReferenceString(); + std::string GetArrayReferenceString(); }; void PrintReferences(lldb::SBCommandReturnObject& result, diff --git a/src/llv8.cc b/src/llv8.cc index 4973a461..7d4443cd 100644 --- a/src/llv8.cc +++ b/src/llv8.cc @@ -3,9 +3,14 @@ #include #include #include +#include +#include +#include +#include "deps/rang/include/rang.hpp" #include "llv8-inl.h" #include "llv8.h" +#include "src/settings.h" namespace llnode { namespace v8 { @@ -377,6 +382,7 @@ std::string JSFunction::GetDebugLine(std::string args, Error& err) { std::string JSFunction::Inspect(InspectOptions* options, Error& err) { std::string res = "detailed) { @@ -385,9 +391,12 @@ std::string JSFunction::Inspect(InspectOptions* options, Error& err) { Context context(context_obj); - char tmp[128]; - snprintf(tmp, sizeof(tmp), "\n context=0x%016" PRIx64, context.raw()); - res += tmp; + std::stringstream ss; + ss << rang::fg::magenta << res << rang::fg::reset << rang::style::bold + << rang::fg::yellow << "\n context" << rang::fg::reset + << rang::style::reset << "=" << rang::fg::cyan << "0x%016" PRIx64 + << rang::fg::reset; + res = ss.str(); { InspectOptions ctx_options; @@ -415,9 +424,13 @@ std::string JSFunction::Inspect(InspectOptions* options, Error& err) { res += source + "\n"; } } + res = res + ">"; + } else { + std::stringstream ss; + ss << rang::fg::yellow << res << ">" << rang::fg::reset; + res = ss.str(); } - - return res + ">"; + return res; } @@ -495,11 +508,18 @@ std::string JSRegExp::Inspect(InspectOptions* options, Error& err) { // Print properties in detailed mode if (options->detailed) { + std::stringstream ss; + ss << rang::fg::magenta << res << rang::fg::reset; + res = ss.str(); + res += " " + InspectProperties(err); + res += ">"; if (err.Fail()) return std::string(); + } else { + std::stringstream ss; + ss << rang::fg::yellow << res + ">" << rang::fg::reset; + res = ss.str(); } - - res += ">"; return res; } @@ -779,13 +799,29 @@ std::string HeapObject::Inspect(InspectOptions* options, Error& err) { snprintf(buf, sizeof(buf), "0x%016" PRIx64 "(map=0x%016" PRIx64 "):", raw(), map.raw()); } else { - snprintf(buf, sizeof(buf), "0x%016" PRIx64 ":", raw()); + std::stringstream ss; + ss << rang::fg::cyan << "0x" << std::hex << raw() << std::dec + << rang::fg::reset; + snprintf(buf, sizeof(buf), "%s:", ss.str().c_str()); } + std::string pre = buf; - if (type == v8()->types()->kGlobalObjectType) return pre + ""; - if (type == v8()->types()->kGlobalProxyType) return pre + ""; - if (type == v8()->types()->kCodeType) return pre + ""; + if (type == v8()->types()->kGlobalObjectType) { + std::stringstream ss; + ss << rang::fg::yellow << "" << rang::fg::reset; + return pre + ss.str(); + } + if (type == v8()->types()->kGlobalProxyType) { + std::stringstream ss; + ss << rang::fg::yellow << "" << rang::fg::reset; + return pre + ss.str(); + } + if (type == v8()->types()->kCodeType) { + std::stringstream ss; + ss << rang::fg::yellow << "" << rang::fg::reset; + return pre + ss.str(); + } if (type == v8()->types()->kMapType) { Map m(this); return pre + m.Inspect(options, err); @@ -854,7 +890,10 @@ std::string HeapObject::Inspect(InspectOptions* options, Error& err) { Error::PrintInDebugMode( "Unknown HeapObject Type %" PRId64 " at 0x%016" PRIx64 "", type, raw()); - return pre + ""; + + std::stringstream ss; + ss << rang::fg::yellow << "" << rang::fg::reset; + return pre + ss.str().c_str(); } @@ -953,7 +992,11 @@ std::string HeapObject::GetTypeName(Error& err) { } -std::string Smi::Inspect(Error& err) { return ""; } +std::string Smi::Inspect(Error& err) { + std::stringstream ss; + ss << rang::fg::yellow << "" << rang::fg::reset; + return ss.str(); +} std::string HeapNumber::ToString(bool whole, Error& err) { @@ -966,7 +1009,10 @@ std::string HeapNumber::ToString(bool whole, Error& err) { std::string HeapNumber::Inspect(Error& err) { - return ""; + std::stringstream ss; + ss << rang::fg::yellow << "" + << rang::fg::reset; + return ss.str(); } @@ -1023,7 +1069,9 @@ std::string String::Inspect(InspectOptions* options, Error& err) { if (len != 0 && val.length() > len) val = val.substr(0, len) + "..."; - return ""; + std::stringstream ss; + ss << rang::fg::yellow << "" << rang::fg::reset; + return ss.str(); } @@ -1035,11 +1083,20 @@ std::string FixedArray::Inspect(InspectOptions* options, Error& err) { if (err.Fail()) return std::string(); if (options->detailed) { + std::stringstream ss; + ss << rang::fg::magenta << res; std::string contents = InspectContents(length_smi.GetValue(), err); - if (!contents.empty()) res += " contents={\n" + contents + "}"; + if (!contents.empty()) { + ss << " contents" << rang::fg::reset << "={\n" + contents + "}"; + res = ss.str(); + } + res += ">"; + } else { + std::stringstream ss; + ss << rang::fg::yellow << res + ">" << rang::fg::reset; + res = ss.str(); } - - return res + ">"; + return res; } @@ -1054,8 +1111,11 @@ std::string FixedArray::InspectContents(int length, Error& err) { if (!res.empty()) res += ",\n"; char tmp[128]; - snprintf(tmp, sizeof(tmp), " [%d]=", i); - res += tmp + value.Inspect(&options, err); + + std::stringstream ss; + ss << rang::style::bold << rang::fg::yellow << " [" << i << "]" + << rang::fg::reset << rang::style::reset << "="; + res += ss.str() + value.Inspect(&options, err); if (err.Fail()) return std::string(); } @@ -1125,12 +1185,14 @@ std::string Context::Inspect(InspectOptions* options, Error& err) { HeapObject heap_previous = HeapObject(previous); if (heap_previous.Check()) { - char tmp[128]; - snprintf( - tmp, sizeof(tmp), - (options->get_indent_spaces() + "(previous)=0x%016" PRIx64).c_str(), - previous.raw()); - res += std::string(tmp) + ":,"; + std::stringstream ss; + ss << rang::style::bold << rang::fg::yellow << options->get_indent_spaces() + << "(previous)" << rang::fg::reset << rang::style::reset << "=" + << rang::fg::cyan << "0x" << std::hex << previous.raw() << std::dec + << rang::fg::reset << ":" << rang::fg::yellow << "" + << rang::fg::reset << ","; + + res += ss.str(); } if (!res.empty()) res += "\n"; @@ -1138,24 +1200,27 @@ std::string Context::Inspect(InspectOptions* options, Error& err) { if (v8()->context()->hasClosure()) { JSFunction closure = Closure(err); if (err.Fail()) return std::string(); - char tmp[128]; - snprintf( - tmp, sizeof(tmp), - (options->get_indent_spaces() + "(closure)=0x%016" PRIx64 " {").c_str(), - closure.raw()); - res += tmp; + + std::stringstream ss; + ss << rang::style::bold << rang::fg::yellow << options->get_indent_spaces() + << "(closure)" << rang::fg::reset << rang::style::reset << "=" + << rang::fg::cyan << "0x" << std::hex << closure.raw() << std::dec + << rang::fg::reset << " {"; + res += ss.str(); InspectOptions closure_options; res += closure.Inspect(&closure_options, err) + "}"; if (err.Fail()) return std::string(); } else { - char tmp[128]; - snprintf( - tmp, sizeof(tmp), - (options->get_indent_spaces() + "(scope_info)=0x%016" PRIx64).c_str(), - scope.raw()); - - res += std::string(tmp) + ":get_indent_spaces() << "(scope_info)" << rang::fg::reset + << rang::style::reset << "=" << rang::fg::cyan << "0x" << std::hex + << scope.raw() << std::dec << rang::fg::yellow; + res += ss.str() + ":get_indent_spaces() + name.ToString(err) + "="; + std::stringstream ss; + ss << options->get_indent_spaces() << rang::style::bold << rang::fg::yellow + << name.ToString(err) << rang::fg::reset << rang::style::reset; + res += ss.str() + "="; if (err.Fail()) return std::string(); Value value = it.GetValue(err); @@ -1197,21 +1269,38 @@ std::string Oddball::Inspect(Error& err) { if (err.Fail()) return std::string(); int64_t kind_val = kind.GetValue(); - if (kind_val == v8()->oddball()->kException) return ""; - if (kind_val == v8()->oddball()->kFalse) return ""; - if (kind_val == v8()->oddball()->kTrue) return ""; - if (kind_val == v8()->oddball()->kUndefined) return ""; - if (kind_val == v8()->oddball()->kNull) return ""; - if (kind_val == v8()->oddball()->kTheHole) return ""; - if (kind_val == v8()->oddball()->kUninitialized) return ""; - return ""; + std::string str; + if (kind_val == v8()->oddball()->kException) + str = ""; + else if (kind_val == v8()->oddball()->kFalse) + str = ""; + else if (kind_val == v8()->oddball()->kTrue) + str = ""; + else if (kind_val == v8()->oddball()->kUndefined) + str = ""; + else if (kind_val == v8()->oddball()->kNull) + str = ""; + else if (kind_val == v8()->oddball()->kTheHole) + str = ""; + else if (kind_val == v8()->oddball()->kUninitialized) + str = ""; + else + str = ""; + + std::stringstream ss; + ss << rang::fg::yellow << str << rang::fg::reset; + return ss.str(); } std::string JSArrayBuffer::Inspect(InspectOptions* options, Error& err) { bool neutered = WasNeutered(err); if (err.Fail()) return std::string(); - if (neutered) return ""; + if (neutered) { + std::stringstream ss; + ss << rang::fg::yellow << "" << rang::fg::reset; + return ss.str(); + } int64_t data = BackingStore(err); if (err.Fail()) return std::string(); @@ -1229,7 +1318,11 @@ std::string JSArrayBuffer::Inspect(InspectOptions* options, Error& err) { std::string res; res += tmp; if (options->detailed) { - res += ": [\n "; + std::stringstream ss; + ss << rang::fg::magenta << res + ":" << rang::fg::yellow; + res = ss.str(); + + res += " [\n "; int display_length = std::min(byte_length, options->length); res += v8()->LoadBytes(data, display_length, err); @@ -1237,12 +1330,19 @@ std::string JSArrayBuffer::Inspect(InspectOptions* options, Error& err) { if (display_length < byte_length) { res += " ..."; } - res += "\n]>"; + + res += "\n]"; + ss.str(""); + ss.clear(); + ss << res << rang::fg::reset; + + res = ss.str(); + return res + ">"; } else { - res += ">"; + std::stringstream ss; + ss << rang::fg::yellow << res + ">" << rang::fg::reset; + return ss.str(); } - - return res; } @@ -1253,7 +1353,11 @@ std::string JSArrayBufferView::Inspect(InspectOptions* options, Error& err) { bool neutered = buf.WasNeutered(err); if (err.Fail()) return std::string(); - if (neutered) return ""; + if (neutered) { + std::stringstream ss; + ss << rang::fg::yellow << "" << rang::fg::reset; + return ss.str().c_str(); + } int64_t data = buf.BackingStore(err); if (err.Fail()) return std::string(); @@ -1287,7 +1391,11 @@ std::string JSArrayBufferView::Inspect(InspectOptions* options, Error& err) { std::string res; res += tmp; if (options->detailed) { - res += ": [\n "; + std::stringstream ss; + ss << rang::fg::magenta << res + ":" << rang::fg::yellow; + res = ss.str(); + + res += " [\n "; int display_length = std::min(byte_length, options->length); res += v8()->LoadBytes(data + byte_offset, display_length, err); @@ -1295,11 +1403,20 @@ std::string JSArrayBufferView::Inspect(InspectOptions* options, Error& err) { if (display_length < byte_length) { res += " ..."; } - res += "\n]>"; + + res += "\n]"; + + ss.str(""); + ss.clear(); + ss << res << rang::fg::reset; + res = ss.str(); + + return res + ">"; } else { - res += ">"; + std::stringstream ss; + ss << rang::fg::yellow << res + ">" << rang::fg::reset; + return ss.str(); } - return res; } @@ -1326,13 +1443,18 @@ std::string Map::Inspect(InspectOptions* options, Error& err) { if (err.Fail()) return std::string(); char tmp[256]; - snprintf(tmp, sizeof(tmp), - "(own_descriptors_count), in_object_properties_or_constructor.c_str(), static_cast(in_object_properties_or_constructor_index), static_cast(instance_size), descriptors_obj.raw()); + if (!options->detailed) { return std::string(tmp) + ">"; } @@ -1373,8 +1495,11 @@ std::string JSObject::Inspect(InspectOptions* options, Error& err) { int64_t constructor_type = constructor_obj.GetType(err); if (err.Fail()) return std::string(); - if (constructor_type != v8()->types()->kJSFunctionType) - return ""; + if (constructor_type != v8()->types()->kJSFunctionType) { + std::stringstream ss; + ss << rang::fg::yellow << "" << rang::fg::reset; + return ss.str().c_str(); + } JSFunction constructor(constructor_obj); @@ -1383,17 +1508,30 @@ std::string JSObject::Inspect(InspectOptions* options, Error& err) { // Print properties in detailed mode if (options->detailed) { + std::stringstream ss; + ss << rang::fg::magenta << res << rang::fg::reset; + res = ss.str().c_str(); + res += " " + InspectProperties(err); if (err.Fail()) return std::string(); std::string fields = InspectInternalFields(err); if (err.Fail()) return std::string(); - if (!fields.empty()) res += "\n internal fields {\n" + fields + "}"; + if (!fields.empty()) { + std::stringstream ss; + ss << rang::fg::magenta << "\n internal fields" << rang::fg::reset + << " {" << std::endl + << fields << "}"; + res += ss.str().c_str(); + } + return res + ">"; + } else { + std::stringstream ss; + ss << rang::fg::yellow << res << ">" << rang::fg::reset; + res = ss.str().c_str(); + return res; } - - res += ">"; - return res; } @@ -1422,6 +1560,7 @@ std::string JSObject::InspectInternalFields(Error& err) { instance_size -= in_object_props * v8()->common()->kPointerSize; std::string res; + std::stringstream ss; for (int64_t off = v8()->js_object()->kInternalFieldsOffset; off < instance_size; off += v8()->common()->kPointerSize) { int64_t field = LoadField(off, err); @@ -1431,7 +1570,11 @@ std::string JSObject::InspectInternalFields(Error& err) { snprintf(tmp, sizeof(tmp), " 0x%016" PRIx64, field); if (!res.empty()) res += ",\n "; - res += tmp; + + ss.str(""); + ss.clear(); + ss << rang::fg::cyan << tmp << rang::fg::reset; + res += ss.str(); } return res; @@ -1444,7 +1587,13 @@ std::string JSObject::InspectProperties(Error& err) { std::string elems = InspectElements(err); if (err.Fail()) return std::string(); - if (!elems.empty()) res = "elements {\n" + elems + "}"; + if (!elems.empty()) { + std::stringstream ss; + ss << rang::fg::magenta << "elements" << rang::fg::reset << " {" + << std::endl + << elems << "}"; + res = ss.str(); + } HeapObject map_obj = GetMap(err); if (err.Fail()) return std::string(); @@ -1464,7 +1613,11 @@ std::string JSObject::InspectProperties(Error& err) { if (!props.empty()) { if (!res.empty()) res += "\n "; - res += "properties {\n" + props + "}"; + std::stringstream ss; + ss << rang::fg::magenta << "properties" << rang::fg::reset << " {" + << std::endl + << props << "}"; + res += ss.str().c_str(); } return res; @@ -1506,8 +1659,12 @@ std::string JSObject::InspectElements(int64_t length, Error& err) { if (!res.empty()) res += ",\n"; char tmp[64]; - snprintf(tmp, sizeof(tmp), " [%d]=", static_cast(i)); - res += tmp; + + std::stringstream ss; + ss << rang::style::bold << rang::fg::yellow << " [" + << static_cast(i) << "]" << rang::fg::reset << rang::style::reset + << "="; + res += ss.str(); res += value.Inspect(&options, err); if (err.Fail()) return std::string(); @@ -1529,6 +1686,8 @@ std::string JSObject::InspectDictionary(Error& err) { InspectOptions options; std::string res; + std::stringstream ss; + for (int64_t i = 0; i < length; i++) { Value key = dictionary.GetKey(i, err); if (err.Fail()) return std::string(); @@ -1543,7 +1702,11 @@ std::string JSObject::InspectDictionary(Error& err) { if (!res.empty()) res += ",\n"; - res += " ." + key.ToString(err) + "="; + ss.str(""); + ss.clear(); + ss << rang::style::bold << rang::fg::yellow << " ." + key.ToString(err) + << rang::fg::reset << rang::style::reset; + res += ss.str() + "="; if (err.Fail()) return std::string(); res += value.Inspect(&options, err); @@ -1576,6 +1739,7 @@ std::string JSObject::InspectDescriptors(Map map, Error& err) { InspectOptions options; std::string res; + std::stringstream ss; for (int64_t i = 0; i < own_descriptors_count; i++) { Smi details = descriptors.GetDetails(i, err); if (err.Fail()) return std::string(); @@ -1585,7 +1749,11 @@ std::string JSObject::InspectDescriptors(Map map, Error& err) { if (!res.empty()) res += ",\n"; - res += " ." + key.ToString(err) + "="; + ss.str(""); + ss.clear(); + ss << rang::style::bold << rang::fg::yellow << " ." + key.ToString(err) + << rang::fg::reset << rang::style::reset; + res += ss.str() + "="; if (err.Fail()) return std::string(); if (descriptors.IsConstFieldDetails(details) || @@ -2045,15 +2213,24 @@ std::string JSArray::Inspect(InspectOptions* options, Error& err) { if (err.Fail()) return std::string(); std::string res = ""; + if (!elems.empty()) res += " {\n" + elems + "}>"; + return res; + } else { + std::stringstream ss; + ss << rang::fg::yellow << res + ">" << rang::fg::reset; + res = ss.str().c_str(); + return res; + } } } // namespace v8 diff --git a/src/settings.cc b/src/settings.cc new file mode 100644 index 00000000..b27a01df --- /dev/null +++ b/src/settings.cc @@ -0,0 +1,35 @@ +#include "src/settings.h" +#include "deps/rang/include/rang.hpp" + +namespace llnode { + +Settings::Settings() {} +Settings::~Settings() {} + +Settings Settings::instance; +Settings* Settings::GetSettings() { return &Settings::instance; } + +std::string Settings::SetColor(std::string option) { + if (option == "auto" || option == "always" || option == "never") + color = option; + + if (ShouldUseColor()) + rang::setControlMode(rang::control::Force); + else + rang::setControlMode(rang::control::Off); + + return color; +} + +bool Settings::ShouldUseColor() { +#ifdef NO_COLOR_OUTPUT + return false; +#endif + if (color == "always" || + (color == "auto" && rang::rang_implementation::supportsColor() && + rang::rang_implementation::isTerminal(std::cout.rdbuf()))) + return true; + return false; +} + +} // namespace llnode diff --git a/src/settings.h b/src/settings.h new file mode 100644 index 00000000..5fcc8e02 --- /dev/null +++ b/src/settings.h @@ -0,0 +1,32 @@ +#ifndef SRC_SETTINGS_H +#define SRC_SETTINGS_H + +#include + +namespace llnode { + +// Use singleton pattern to avoid multiples instatiations +// of Settings +class Settings { + private: + static Settings instance; + + // Private constructors to prevent instantiations + Settings(); + ~Settings(); + Settings(const Settings&) = delete; + + // Override assignment operator to avoid copies + Settings& operator=(const Settings&) = delete; + + std::string color = "auto"; + + public: + static Settings* GetSettings(); + std::string SetColor(std::string option); + std::string GetColor() { return color; }; + bool ShouldUseColor(); +}; + +} // namespace llnode +#endif // SRC_SETTINGS_H