mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 19:47:34 +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
|
@ -79,7 +79,8 @@ ThrowCompletionOr<Value> Console::warn()
|
|||
// 1.1.2. clear(), https://console.spec.whatwg.org/#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.)
|
||||
if (m_client)
|
||||
|
@ -107,12 +108,7 @@ ThrowCompletionOr<Value> Console::trace()
|
|||
StringBuilder builder;
|
||||
auto data = vm_arguments();
|
||||
auto formatted_data = TRY(m_client->formatter(data));
|
||||
for (auto const& item : formatted_data) {
|
||||
if (!builder.is_empty())
|
||||
builder.append(' ');
|
||||
builder.append(TRY(item.to_string(global_object())));
|
||||
}
|
||||
trace.label = builder.to_string();
|
||||
trace.label = TRY(value_vector_to_string(formatted_data));
|
||||
}
|
||||
|
||||
// 3. Perform Printer("trace", « trace »).
|
||||
|
@ -221,6 +217,88 @@ ThrowCompletionOr<Value> Console::assert_()
|
|||
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> arguments;
|
||||
|
@ -257,6 +335,17 @@ void Console::output_debug_message([[maybe_unused]] LogLevel log_level, [[maybe_
|
|||
#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()
|
||||
{
|
||||
return global_object().vm();
|
||||
|
|
|
@ -43,6 +43,10 @@ public:
|
|||
Warn,
|
||||
};
|
||||
|
||||
struct Group {
|
||||
String label;
|
||||
};
|
||||
|
||||
struct Trace {
|
||||
String label;
|
||||
Vector<String> stack;
|
||||
|
@ -71,14 +75,21 @@ public:
|
|||
ThrowCompletionOr<Value> count();
|
||||
ThrowCompletionOr<Value> count_reset();
|
||||
ThrowCompletionOr<Value> assert_();
|
||||
ThrowCompletionOr<Value> group();
|
||||
ThrowCompletionOr<Value> group_collapsed();
|
||||
ThrowCompletionOr<Value> group_end();
|
||||
|
||||
void output_debug_message(LogLevel log_level, String output) const;
|
||||
|
||||
private:
|
||||
ThrowCompletionOr<String> value_vector_to_string(Vector<Value>&);
|
||||
|
||||
GlobalObject& m_global_object;
|
||||
ConsoleClient* m_client { nullptr };
|
||||
|
||||
HashMap<String, unsigned> m_counters;
|
||||
|
||||
Vector<Group> m_group_stack;
|
||||
};
|
||||
|
||||
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<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 end_group() = 0;
|
||||
|
||||
protected:
|
||||
virtual ~ConsoleClient() = default;
|
||||
|
|
|
@ -240,6 +240,9 @@ namespace JS {
|
|||
P(getYear) \
|
||||
P(global) \
|
||||
P(globalThis) \
|
||||
P(group) \
|
||||
P(groupCollapsed) \
|
||||
P(groupEnd) \
|
||||
P(groups) \
|
||||
P(has) \
|
||||
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.clear, clear, 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()
|
||||
|
@ -98,4 +101,22 @@ JS_DEFINE_NATIVE_FUNCTION(ConsoleObject::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(clear);
|
||||
JS_DECLARE_NATIVE_FUNCTION(assert_);
|
||||
JS_DECLARE_NATIVE_FUNCTION(group);
|
||||
JS_DECLARE_NATIVE_FUNCTION(group_collapsed);
|
||||
JS_DECLARE_NATIVE_FUNCTION(group_end);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue