mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 19:07:35 +00:00
LibJS+WebContent+Browser+js: Implement console.group() methods
This implements: - console.group() - console.groupCollapsed() - console.groupEnd() In the Browser, we use `<details>` for the groups, which is not actually implemented yet, so groups are always open. In the REPL, groups are non-interactive, but still indent any output. This looks weird since the console prompt and return values remain on the far left, but this matches what Node does so it's probably fine. :^) I expect `console.group()` is not used much outside of browsers.
This commit is contained in:
parent
ff5e07d718
commit
d702678d16
10 changed files with 281 additions and 25 deletions
|
@ -111,6 +111,12 @@ void ConsoleWidget::handle_console_messages(i32 start_index, const Vector<String
|
||||||
print_html(message);
|
print_html(message);
|
||||||
} else if (type == "clear") {
|
} else if (type == "clear") {
|
||||||
clear_output();
|
clear_output();
|
||||||
|
} else if (type == "group") {
|
||||||
|
begin_group(message, true);
|
||||||
|
} else if (type == "groupCollapsed") {
|
||||||
|
begin_group(message, false);
|
||||||
|
} else if (type == "groupEnd") {
|
||||||
|
end_group();
|
||||||
} else {
|
} else {
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
@ -138,26 +144,85 @@ void ConsoleWidget::print_source_line(StringView source)
|
||||||
void ConsoleWidget::print_html(StringView line)
|
void ConsoleWidget::print_html(StringView line)
|
||||||
{
|
{
|
||||||
StringBuilder builder;
|
StringBuilder builder;
|
||||||
|
|
||||||
|
int parent_id = m_group_stack.is_empty() ? 0 : m_group_stack.last().id;
|
||||||
|
if (parent_id == 0) {
|
||||||
|
builder.append(R"~~~(
|
||||||
|
var parentGroup = document.body;
|
||||||
|
)~~~");
|
||||||
|
} else {
|
||||||
|
builder.appendff(R"~~~(
|
||||||
|
var parentGroup = document.getElementById("group_{}");
|
||||||
|
)~~~",
|
||||||
|
parent_id);
|
||||||
|
}
|
||||||
|
|
||||||
builder.append(R"~~~(
|
builder.append(R"~~~(
|
||||||
var p = document.createElement("p");
|
var p = document.createElement("p");
|
||||||
p.innerHTML = ")~~~");
|
p.innerHTML = ")~~~");
|
||||||
builder.append_escaped_for_json(line);
|
builder.append_escaped_for_json(line);
|
||||||
builder.append(R"~~~("
|
builder.append(R"~~~("
|
||||||
document.body.appendChild(p);
|
parentGroup.appendChild(p);
|
||||||
)~~~");
|
)~~~");
|
||||||
m_output_view->run_javascript(builder.string_view());
|
m_output_view->run_javascript(builder.string_view());
|
||||||
// FIXME: Make it scroll to the bottom, using `window.scrollTo()` in the JS above.
|
// FIXME: Make it scroll to the bottom, using `window.scrollTo()` in the JS above.
|
||||||
// We used to call `m_output_view->scroll_to_bottom();` here, but that does not work because
|
// We used to call `m_output_view->scroll_to_bottom();` here, but that does not work because
|
||||||
// it runs synchronously, meaning it happens before the HTML is output via IPC above.
|
// it runs synchronously, meaning it happens before the HTML is output via IPC above.
|
||||||
|
// (See also: begin_group())
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConsoleWidget::clear_output()
|
void ConsoleWidget::clear_output()
|
||||||
{
|
{
|
||||||
|
m_group_stack.clear();
|
||||||
m_output_view->run_javascript(R"~~~(
|
m_output_view->run_javascript(R"~~~(
|
||||||
document.body.innerHTML = "";
|
document.body.innerHTML = "";
|
||||||
)~~~");
|
)~~~");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConsoleWidget::begin_group(StringView label, bool start_expanded)
|
||||||
|
{
|
||||||
|
StringBuilder builder;
|
||||||
|
int parent_id = m_group_stack.is_empty() ? 0 : m_group_stack.last().id;
|
||||||
|
if (parent_id == 0) {
|
||||||
|
builder.append(R"~~~(
|
||||||
|
var parentGroup = document.body;
|
||||||
|
)~~~");
|
||||||
|
} else {
|
||||||
|
builder.appendff(R"~~~(
|
||||||
|
var parentGroup = document.getElementById("group_{}");
|
||||||
|
)~~~",
|
||||||
|
parent_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Group group;
|
||||||
|
group.id = m_next_group_id++;
|
||||||
|
group.label = label;
|
||||||
|
|
||||||
|
builder.appendff(R"~~~(
|
||||||
|
var group = document.createElement("details");
|
||||||
|
group.id = "group_{}";
|
||||||
|
var label = document.createElement("summary");
|
||||||
|
label.innerText = ")~~~",
|
||||||
|
group.id);
|
||||||
|
builder.append_escaped_for_json(label);
|
||||||
|
builder.append(R"~~~(";
|
||||||
|
group.appendChild(label);
|
||||||
|
parentGroup.appendChild(group);
|
||||||
|
)~~~");
|
||||||
|
|
||||||
|
if (start_expanded)
|
||||||
|
builder.append("group.open = true;");
|
||||||
|
|
||||||
|
m_output_view->run_javascript(builder.string_view());
|
||||||
|
// FIXME: Scroll console to bottom - see note in print_html()
|
||||||
|
m_group_stack.append(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConsoleWidget::end_group()
|
||||||
|
{
|
||||||
|
m_group_stack.take_last();
|
||||||
|
}
|
||||||
|
|
||||||
void ConsoleWidget::reset()
|
void ConsoleWidget::reset()
|
||||||
{
|
{
|
||||||
clear_output();
|
clear_output();
|
||||||
|
|
|
@ -33,6 +33,8 @@ private:
|
||||||
|
|
||||||
void request_console_messages();
|
void request_console_messages();
|
||||||
void clear_output();
|
void clear_output();
|
||||||
|
void begin_group(StringView label, bool start_expanded);
|
||||||
|
void end_group();
|
||||||
|
|
||||||
RefPtr<GUI::TextBox> m_input;
|
RefPtr<GUI::TextBox> m_input;
|
||||||
RefPtr<Web::OutOfProcessWebView> m_output_view;
|
RefPtr<Web::OutOfProcessWebView> m_output_view;
|
||||||
|
@ -40,6 +42,13 @@ private:
|
||||||
i32 m_highest_notified_message_index { -1 };
|
i32 m_highest_notified_message_index { -1 };
|
||||||
i32 m_highest_received_message_index { -1 };
|
i32 m_highest_received_message_index { -1 };
|
||||||
bool m_waiting_for_messages { false };
|
bool m_waiting_for_messages { false };
|
||||||
|
|
||||||
|
struct Group {
|
||||||
|
int id { 0 };
|
||||||
|
String label;
|
||||||
|
};
|
||||||
|
Vector<Group> m_group_stack;
|
||||||
|
int m_next_group_id { 1 };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,8 @@ ThrowCompletionOr<Value> Console::warn()
|
||||||
// 1.1.2. clear(), https://console.spec.whatwg.org/#clear
|
// 1.1.2. clear(), https://console.spec.whatwg.org/#clear
|
||||||
Value Console::clear()
|
Value Console::clear()
|
||||||
{
|
{
|
||||||
// 1. TODO: Empty the appropriate group stack.
|
// 1. Empty the appropriate group stack.
|
||||||
|
m_group_stack.clear();
|
||||||
|
|
||||||
// 2. If possible for the environment, clear the console. (Otherwise, do nothing.)
|
// 2. If possible for the environment, clear the console. (Otherwise, do nothing.)
|
||||||
if (m_client)
|
if (m_client)
|
||||||
|
@ -107,12 +108,7 @@ ThrowCompletionOr<Value> Console::trace()
|
||||||
StringBuilder builder;
|
StringBuilder builder;
|
||||||
auto data = vm_arguments();
|
auto data = vm_arguments();
|
||||||
auto formatted_data = TRY(m_client->formatter(data));
|
auto formatted_data = TRY(m_client->formatter(data));
|
||||||
for (auto const& item : formatted_data) {
|
trace.label = TRY(value_vector_to_string(formatted_data));
|
||||||
if (!builder.is_empty())
|
|
||||||
builder.append(' ');
|
|
||||||
builder.append(TRY(item.to_string(global_object())));
|
|
||||||
}
|
|
||||||
trace.label = builder.to_string();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Perform Printer("trace", « trace »).
|
// 3. Perform Printer("trace", « trace »).
|
||||||
|
@ -221,6 +217,88 @@ ThrowCompletionOr<Value> Console::assert_()
|
||||||
return js_undefined();
|
return js_undefined();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1.3.1. group(...data), https://console.spec.whatwg.org/#group
|
||||||
|
ThrowCompletionOr<Value> Console::group()
|
||||||
|
{
|
||||||
|
// 1. Let group be a new group.
|
||||||
|
Group group;
|
||||||
|
|
||||||
|
// 2. If data is not empty, let groupLabel be the result of Formatter(data).
|
||||||
|
String group_label;
|
||||||
|
auto data = vm_arguments();
|
||||||
|
if (!data.is_empty()) {
|
||||||
|
auto formatted_data = TRY(m_client->formatter(data));
|
||||||
|
group_label = TRY(value_vector_to_string(formatted_data));
|
||||||
|
}
|
||||||
|
// ... Otherwise, let groupLabel be an implementation-chosen label representing a group.
|
||||||
|
else {
|
||||||
|
group_label = "Group";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Incorporate groupLabel as a label for group.
|
||||||
|
group.label = group_label;
|
||||||
|
|
||||||
|
// 4. Optionally, if the environment supports interactive groups, group should be expanded by default.
|
||||||
|
// NOTE: This is handled in Printer.
|
||||||
|
|
||||||
|
// 5. Perform Printer("group", « group »).
|
||||||
|
if (m_client)
|
||||||
|
TRY(m_client->printer(LogLevel::Group, group));
|
||||||
|
|
||||||
|
// 6. Push group onto the appropriate group stack.
|
||||||
|
m_group_stack.append(group);
|
||||||
|
|
||||||
|
return js_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.3.2. groupCollapsed(...data), https://console.spec.whatwg.org/#groupcollapsed
|
||||||
|
ThrowCompletionOr<Value> Console::group_collapsed()
|
||||||
|
{
|
||||||
|
// 1. Let group be a new group.
|
||||||
|
Group group;
|
||||||
|
|
||||||
|
// 2. If data is not empty, let groupLabel be the result of Formatter(data).
|
||||||
|
String group_label;
|
||||||
|
auto data = vm_arguments();
|
||||||
|
if (!data.is_empty()) {
|
||||||
|
auto formatted_data = TRY(m_client->formatter(data));
|
||||||
|
group_label = TRY(value_vector_to_string(formatted_data));
|
||||||
|
}
|
||||||
|
// ... Otherwise, let groupLabel be an implementation-chosen label representing a group.
|
||||||
|
else {
|
||||||
|
group_label = "Group";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Incorporate groupLabel as a label for group.
|
||||||
|
group.label = group_label;
|
||||||
|
|
||||||
|
// 4. Optionally, if the environment supports interactive groups, group should be collapsed by default.
|
||||||
|
// NOTE: This is handled in Printer.
|
||||||
|
|
||||||
|
// 5. Perform Printer("groupCollapsed", « group »).
|
||||||
|
if (m_client)
|
||||||
|
TRY(m_client->printer(LogLevel::GroupCollapsed, group));
|
||||||
|
|
||||||
|
// 6. Push group onto the appropriate group stack.
|
||||||
|
m_group_stack.append(group);
|
||||||
|
|
||||||
|
return js_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.3.3. groupEnd(), https://console.spec.whatwg.org/#groupend
|
||||||
|
ThrowCompletionOr<Value> Console::group_end()
|
||||||
|
{
|
||||||
|
if (m_group_stack.is_empty())
|
||||||
|
return js_undefined();
|
||||||
|
|
||||||
|
// 1. Pop the last group from the group stack.
|
||||||
|
m_group_stack.take_last();
|
||||||
|
if (m_client)
|
||||||
|
m_client->end_group();
|
||||||
|
|
||||||
|
return js_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
Vector<Value> Console::vm_arguments()
|
Vector<Value> Console::vm_arguments()
|
||||||
{
|
{
|
||||||
Vector<Value> arguments;
|
Vector<Value> arguments;
|
||||||
|
@ -257,6 +335,17 @@ void Console::output_debug_message([[maybe_unused]] LogLevel log_level, [[maybe_
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThrowCompletionOr<String> Console::value_vector_to_string(Vector<Value>& values)
|
||||||
|
{
|
||||||
|
StringBuilder builder;
|
||||||
|
for (auto const& item : values) {
|
||||||
|
if (!builder.is_empty())
|
||||||
|
builder.append(' ');
|
||||||
|
builder.append(TRY(item.to_string(global_object())));
|
||||||
|
}
|
||||||
|
return builder.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
VM& ConsoleClient::vm()
|
VM& ConsoleClient::vm()
|
||||||
{
|
{
|
||||||
return global_object().vm();
|
return global_object().vm();
|
||||||
|
|
|
@ -43,6 +43,10 @@ public:
|
||||||
Warn,
|
Warn,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Group {
|
||||||
|
String label;
|
||||||
|
};
|
||||||
|
|
||||||
struct Trace {
|
struct Trace {
|
||||||
String label;
|
String label;
|
||||||
Vector<String> stack;
|
Vector<String> stack;
|
||||||
|
@ -71,14 +75,21 @@ public:
|
||||||
ThrowCompletionOr<Value> count();
|
ThrowCompletionOr<Value> count();
|
||||||
ThrowCompletionOr<Value> count_reset();
|
ThrowCompletionOr<Value> count_reset();
|
||||||
ThrowCompletionOr<Value> assert_();
|
ThrowCompletionOr<Value> assert_();
|
||||||
|
ThrowCompletionOr<Value> group();
|
||||||
|
ThrowCompletionOr<Value> group_collapsed();
|
||||||
|
ThrowCompletionOr<Value> group_end();
|
||||||
|
|
||||||
void output_debug_message(LogLevel log_level, String output) const;
|
void output_debug_message(LogLevel log_level, String output) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
ThrowCompletionOr<String> value_vector_to_string(Vector<Value>&);
|
||||||
|
|
||||||
GlobalObject& m_global_object;
|
GlobalObject& m_global_object;
|
||||||
ConsoleClient* m_client { nullptr };
|
ConsoleClient* m_client { nullptr };
|
||||||
|
|
||||||
HashMap<String, unsigned> m_counters;
|
HashMap<String, unsigned> m_counters;
|
||||||
|
|
||||||
|
Vector<Group> m_group_stack;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConsoleClient {
|
class ConsoleClient {
|
||||||
|
@ -88,11 +99,14 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using PrinterArguments = Variant<Console::Group, Console::Trace, Vector<Value>>;
|
||||||
|
|
||||||
ThrowCompletionOr<Value> logger(Console::LogLevel log_level, Vector<Value>& args);
|
ThrowCompletionOr<Value> logger(Console::LogLevel log_level, Vector<Value>& args);
|
||||||
ThrowCompletionOr<Vector<Value>> formatter(Vector<Value>& args);
|
ThrowCompletionOr<Vector<Value>> formatter(Vector<Value>& args);
|
||||||
virtual ThrowCompletionOr<Value> printer(Console::LogLevel log_level, Variant<Vector<Value>, Console::Trace>) = 0;
|
virtual ThrowCompletionOr<Value> printer(Console::LogLevel log_level, PrinterArguments) = 0;
|
||||||
|
|
||||||
virtual void clear() = 0;
|
virtual void clear() = 0;
|
||||||
|
virtual void end_group() = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~ConsoleClient() = default;
|
virtual ~ConsoleClient() = default;
|
||||||
|
|
|
@ -240,6 +240,9 @@ namespace JS {
|
||||||
P(getYear) \
|
P(getYear) \
|
||||||
P(global) \
|
P(global) \
|
||||||
P(globalThis) \
|
P(globalThis) \
|
||||||
|
P(group) \
|
||||||
|
P(groupCollapsed) \
|
||||||
|
P(groupEnd) \
|
||||||
P(groups) \
|
P(groups) \
|
||||||
P(has) \
|
P(has) \
|
||||||
P(hasIndices) \
|
P(hasIndices) \
|
||||||
|
|
|
@ -32,6 +32,9 @@ void ConsoleObject::initialize(GlobalObject& global_object)
|
||||||
define_native_function(vm.names.countReset, count_reset, 0, attr);
|
define_native_function(vm.names.countReset, count_reset, 0, attr);
|
||||||
define_native_function(vm.names.clear, clear, 0, attr);
|
define_native_function(vm.names.clear, clear, 0, attr);
|
||||||
define_native_function(vm.names.assert, assert_, 0, attr);
|
define_native_function(vm.names.assert, assert_, 0, attr);
|
||||||
|
define_native_function(vm.names.group, group, 0, attr);
|
||||||
|
define_native_function(vm.names.groupCollapsed, group_collapsed, 0, attr);
|
||||||
|
define_native_function(vm.names.groupEnd, group_end, 0, attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleObject::~ConsoleObject()
|
ConsoleObject::~ConsoleObject()
|
||||||
|
@ -98,4 +101,22 @@ JS_DEFINE_NATIVE_FUNCTION(ConsoleObject::assert_)
|
||||||
return global_object.console().assert_();
|
return global_object.console().assert_();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1.3.1. group(...data), https://console.spec.whatwg.org/#group
|
||||||
|
JS_DEFINE_NATIVE_FUNCTION(ConsoleObject::group)
|
||||||
|
{
|
||||||
|
return global_object.console().group();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.3.2. groupCollapsed(...data), https://console.spec.whatwg.org/#groupcollapsed
|
||||||
|
JS_DEFINE_NATIVE_FUNCTION(ConsoleObject::group_collapsed)
|
||||||
|
{
|
||||||
|
return global_object.console().group_collapsed();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.3.3. groupEnd(), https://console.spec.whatwg.org/#groupend
|
||||||
|
JS_DEFINE_NATIVE_FUNCTION(ConsoleObject::group_end)
|
||||||
|
{
|
||||||
|
return global_object.console().group_end();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,9 @@ private:
|
||||||
JS_DECLARE_NATIVE_FUNCTION(count_reset);
|
JS_DECLARE_NATIVE_FUNCTION(count_reset);
|
||||||
JS_DECLARE_NATIVE_FUNCTION(clear);
|
JS_DECLARE_NATIVE_FUNCTION(clear);
|
||||||
JS_DECLARE_NATIVE_FUNCTION(assert_);
|
JS_DECLARE_NATIVE_FUNCTION(assert_);
|
||||||
|
JS_DECLARE_NATIVE_FUNCTION(group);
|
||||||
|
JS_DECLARE_NATIVE_FUNCTION(group_collapsed);
|
||||||
|
JS_DECLARE_NATIVE_FUNCTION(group_end);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,13 +69,25 @@ void WebContentConsoleClient::handle_input(String const& js_source)
|
||||||
|
|
||||||
void WebContentConsoleClient::print_html(String const& line)
|
void WebContentConsoleClient::print_html(String const& line)
|
||||||
{
|
{
|
||||||
m_message_log.append({ .type = ConsoleOutput::Type::HTML, .html = line });
|
m_message_log.append({ .type = ConsoleOutput::Type::HTML, .data = line });
|
||||||
m_client.async_did_output_js_console_message(m_message_log.size() - 1);
|
m_client.async_did_output_js_console_message(m_message_log.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContentConsoleClient::clear_output()
|
void WebContentConsoleClient::clear_output()
|
||||||
{
|
{
|
||||||
m_message_log.append({ .type = ConsoleOutput::Type::Clear, .html = "" });
|
m_message_log.append({ .type = ConsoleOutput::Type::Clear, .data = "" });
|
||||||
|
m_client.async_did_output_js_console_message(m_message_log.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebContentConsoleClient::begin_group(String const& label, bool start_expanded)
|
||||||
|
{
|
||||||
|
m_message_log.append({ .type = start_expanded ? ConsoleOutput::Type::BeginGroup : ConsoleOutput::Type::BeginGroupCollapsed, .data = label });
|
||||||
|
m_client.async_did_output_js_console_message(m_message_log.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebContentConsoleClient::end_group()
|
||||||
|
{
|
||||||
|
m_message_log.append({ .type = ConsoleOutput::Type::EndGroup, .data = "" });
|
||||||
m_client.async_did_output_js_console_message(m_message_log.size() - 1);
|
m_client.async_did_output_js_console_message(m_message_log.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,9 +119,18 @@ void WebContentConsoleClient::send_messages(i32 start_index)
|
||||||
case ConsoleOutput::Type::Clear:
|
case ConsoleOutput::Type::Clear:
|
||||||
message_types.append("clear");
|
message_types.append("clear");
|
||||||
break;
|
break;
|
||||||
|
case ConsoleOutput::Type::BeginGroup:
|
||||||
|
message_types.append("group");
|
||||||
|
break;
|
||||||
|
case ConsoleOutput::Type::BeginGroupCollapsed:
|
||||||
|
message_types.append("groupCollapsed");
|
||||||
|
break;
|
||||||
|
case ConsoleOutput::Type::EndGroup:
|
||||||
|
message_types.append("groupEnd");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
messages.append(message.html);
|
messages.append(message.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_client.async_did_get_js_console_messages(start_index, message_types, messages);
|
m_client.async_did_get_js_console_messages(start_index, message_types, messages);
|
||||||
|
@ -121,7 +142,7 @@ void WebContentConsoleClient::clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.3. Printer(logLevel, args[, options]), https://console.spec.whatwg.org/#printer
|
// 2.3. Printer(logLevel, args[, options]), https://console.spec.whatwg.org/#printer
|
||||||
JS::ThrowCompletionOr<JS::Value> WebContentConsoleClient::printer(JS::Console::LogLevel log_level, Variant<Vector<JS::Value>, JS::Console::Trace> arguments)
|
JS::ThrowCompletionOr<JS::Value> WebContentConsoleClient::printer(JS::Console::LogLevel log_level, PrinterArguments arguments)
|
||||||
{
|
{
|
||||||
if (log_level == JS::Console::LogLevel::Trace) {
|
if (log_level == JS::Console::LogLevel::Trace) {
|
||||||
auto trace = arguments.get<JS::Console::Trace>();
|
auto trace = arguments.get<JS::Console::Trace>();
|
||||||
|
@ -138,6 +159,12 @@ JS::ThrowCompletionOr<JS::Value> WebContentConsoleClient::printer(JS::Console::L
|
||||||
return JS::js_undefined();
|
return JS::js_undefined();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (log_level == JS::Console::LogLevel::Group || log_level == JS::Console::LogLevel::GroupCollapsed) {
|
||||||
|
auto group = arguments.get<JS::Console::Group>();
|
||||||
|
begin_group(group.label, log_level == JS::Console::LogLevel::Group);
|
||||||
|
return JS::js_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
auto output = String::join(" ", arguments.get<Vector<JS::Value>>());
|
auto output = String::join(" ", arguments.get<Vector<JS::Value>>());
|
||||||
m_console.output_debug_message(log_level, output);
|
m_console.output_debug_message(log_level, output);
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void clear() override;
|
virtual void clear() override;
|
||||||
virtual JS::ThrowCompletionOr<JS::Value> printer(JS::Console::LogLevel log_level, Variant<Vector<JS::Value>, JS::Console::Trace>) override;
|
virtual JS::ThrowCompletionOr<JS::Value> printer(JS::Console::LogLevel log_level, PrinterArguments) override;
|
||||||
|
|
||||||
ClientConnection& m_client;
|
ClientConnection& m_client;
|
||||||
WeakPtr<JS::Interpreter> m_interpreter;
|
WeakPtr<JS::Interpreter> m_interpreter;
|
||||||
|
@ -33,14 +33,19 @@ private:
|
||||||
|
|
||||||
void clear_output();
|
void clear_output();
|
||||||
void print_html(String const& line);
|
void print_html(String const& line);
|
||||||
|
void begin_group(String const& label, bool start_expanded);
|
||||||
|
virtual void end_group() override;
|
||||||
|
|
||||||
struct ConsoleOutput {
|
struct ConsoleOutput {
|
||||||
enum class Type {
|
enum class Type {
|
||||||
HTML,
|
HTML,
|
||||||
Clear
|
Clear,
|
||||||
|
BeginGroup,
|
||||||
|
BeginGroupCollapsed,
|
||||||
|
EndGroup,
|
||||||
};
|
};
|
||||||
Type type;
|
Type type;
|
||||||
String html;
|
String data;
|
||||||
};
|
};
|
||||||
Vector<ConsoleOutput> m_message_log;
|
Vector<ConsoleOutput> m_message_log;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1125,51 +1125,71 @@ public:
|
||||||
virtual void clear() override
|
virtual void clear() override
|
||||||
{
|
{
|
||||||
js_out("\033[3J\033[H\033[2J");
|
js_out("\033[3J\033[H\033[2J");
|
||||||
|
m_group_stack_depth = 0;
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual JS::ThrowCompletionOr<JS::Value> printer(JS::Console::LogLevel log_level, Variant<Vector<JS::Value>, JS::Console::Trace> arguments) override
|
virtual void end_group() override
|
||||||
{
|
{
|
||||||
|
if (m_group_stack_depth > 0)
|
||||||
|
m_group_stack_depth--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.3. Printer(logLevel, args[, options]), https://console.spec.whatwg.org/#printer
|
||||||
|
virtual JS::ThrowCompletionOr<JS::Value> printer(JS::Console::LogLevel log_level, PrinterArguments arguments) override
|
||||||
|
{
|
||||||
|
String indent = String::repeated(" ", m_group_stack_depth);
|
||||||
|
|
||||||
if (log_level == JS::Console::LogLevel::Trace) {
|
if (log_level == JS::Console::LogLevel::Trace) {
|
||||||
auto trace = arguments.get<JS::Console::Trace>();
|
auto trace = arguments.get<JS::Console::Trace>();
|
||||||
StringBuilder builder;
|
StringBuilder builder;
|
||||||
if (!trace.label.is_empty())
|
if (!trace.label.is_empty())
|
||||||
builder.appendff("\033[36;1m{}\033[0m\n", trace.label);
|
builder.appendff("{}\033[36;1m{}\033[0m\n", indent, trace.label);
|
||||||
|
|
||||||
for (auto& function_name : trace.stack)
|
for (auto& function_name : trace.stack)
|
||||||
builder.appendff("-> {}\n", function_name);
|
builder.appendff("{}-> {}\n", indent, function_name);
|
||||||
|
|
||||||
js_outln("{}", builder.string_view());
|
js_outln("{}", builder.string_view());
|
||||||
return JS::js_undefined();
|
return JS::js_undefined();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (log_level == JS::Console::LogLevel::Group || log_level == JS::Console::LogLevel::GroupCollapsed) {
|
||||||
|
auto group = arguments.get<JS::Console::Group>();
|
||||||
|
js_outln("{}\033[36;1m{}\033[0m", indent, group.label);
|
||||||
|
m_group_stack_depth++;
|
||||||
|
return JS::js_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
auto output = String::join(" ", arguments.get<Vector<JS::Value>>());
|
auto output = String::join(" ", arguments.get<Vector<JS::Value>>());
|
||||||
m_console.output_debug_message(log_level, output);
|
m_console.output_debug_message(log_level, output);
|
||||||
|
|
||||||
switch (log_level) {
|
switch (log_level) {
|
||||||
case JS::Console::LogLevel::Debug:
|
case JS::Console::LogLevel::Debug:
|
||||||
js_outln("\033[36;1m{}\033[0m", output);
|
js_outln("{}\033[36;1m{}\033[0m", indent, output);
|
||||||
break;
|
break;
|
||||||
case JS::Console::LogLevel::Error:
|
case JS::Console::LogLevel::Error:
|
||||||
case JS::Console::LogLevel::Assert:
|
case JS::Console::LogLevel::Assert:
|
||||||
js_outln("\033[31;1m{}\033[0m", output);
|
js_outln("{}\033[31;1m{}\033[0m", indent, output);
|
||||||
break;
|
break;
|
||||||
case JS::Console::LogLevel::Info:
|
case JS::Console::LogLevel::Info:
|
||||||
js_outln("(i) {}", output);
|
js_outln("{}(i) {}", indent, output);
|
||||||
break;
|
break;
|
||||||
case JS::Console::LogLevel::Log:
|
case JS::Console::LogLevel::Log:
|
||||||
js_outln("{}", output);
|
js_outln("{}{}", indent, output);
|
||||||
break;
|
break;
|
||||||
case JS::Console::LogLevel::Warn:
|
case JS::Console::LogLevel::Warn:
|
||||||
case JS::Console::LogLevel::CountReset:
|
case JS::Console::LogLevel::CountReset:
|
||||||
js_outln("\033[33;1m{}\033[0m", output);
|
js_outln("{}\033[33;1m{}\033[0m", indent, output);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
js_outln("{}", output);
|
js_outln("{}{}", indent, output);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return JS::js_undefined();
|
return JS::js_undefined();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_group_stack_depth { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue