diff options
author | FalseHonesty <thefalsehonesty@gmail.com> | 2020-05-24 21:35:46 -0400 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-05-25 11:33:52 +0200 |
commit | 2c2ce5be64ad4e4bca0dc72165f2cebbbf6dabf6 (patch) | |
tree | 4174b57351abb1f6439d4988acdb68eb3d9d5b75 | |
parent | 4ad891a078d7ea8e89d4e97177b2a4967cb49438 (diff) | |
download | serenity-2c2ce5be64ad4e4bca0dc72165f2cebbbf6dabf6.zip |
Browser: Add output styles to values printed in the console
This patch adds spans around most of the console's output, allowing
for a global document stylesheet to customize the highlighting of
the console's output. It also adds some basic styling for values
like strings, numbers, and arrays using the system Palette.
Note: This patch simply adds support for highlighting output values,
the lines of JS code printed to console are still unformatted.
-rw-r--r-- | Applications/Browser/ConsoleWidget.cpp | 122 | ||||
-rw-r--r-- | Applications/Browser/ConsoleWidget.h | 2 |
2 files changed, 113 insertions, 11 deletions
diff --git a/Applications/Browser/ConsoleWidget.cpp b/Applications/Browser/ConsoleWidget.cpp index 801904da8d..9743e87bc9 100644 --- a/Applications/Browser/ConsoleWidget.cpp +++ b/Applications/Browser/ConsoleWidget.cpp @@ -54,7 +54,7 @@ ConsoleWidget::ConsoleWidget() auto head_element = create_element(base_document, "head"); html_element->append_child(head_element); auto style_element = create_element(base_document, "style"); - style_element->append_child(adopt(*new Web::Text(base_document, "div { font-family: Csilla; font-weight: lighter; }"))); + style_element->append_child(adopt(*new Web::Text(base_document, create_document_style()))); head_element->append_child(style_element); auto body_element = create_element(base_document, "body"); html_element->append_child(body_element); @@ -72,6 +72,11 @@ ConsoleWidget::ConsoleWidget() m_console_input->on_return_pressed = [this] { auto js_source = m_console_input->text(); + + // FIXME: An is_blank check to check if there is only whitespace would probably be preferable. + if (js_source.is_empty()) + return; + m_console_input->clear(); print_source_line(js_source); @@ -117,11 +122,53 @@ void ConsoleWidget::set_interpreter(WeakPtr<JS::Interpreter> interpreter) clear_output(); } +String ConsoleWidget::create_document_style() +{ + StringBuilder style; + auto palette = this->palette(); + + auto add_class_and_color = [&](const StringView& class_name, Color color, bool bold = false) { + style.append("."); + style.append(class_name); + style.append(" { color: "); + style.append(color.to_string_without_alpha()); + style.append(";"); + + if (bold) { + style.append("font-weight: bold;"); + } + + style.append(" } "); + }; + + add_class_and_color("js-string", palette.syntax_string()); + add_class_and_color("js-number", palette.syntax_number()); + add_class_and_color("js-boolean", palette.syntax_keyword(), true); + add_class_and_color("js-null", palette.syntax_keyword(), true); + add_class_and_color("js-undefined", palette.syntax_keyword(), true); + add_class_and_color("js-array-open", palette.syntax_punctuation()); + add_class_and_color("js-array-close", palette.syntax_punctuation()); + add_class_and_color("js-array-element-separator", palette.syntax_punctuation()); + add_class_and_color("js-object-open", palette.syntax_punctuation()); + add_class_and_color("js-object-close", palette.syntax_punctuation()); + add_class_and_color("js-object-element-separator", palette.syntax_punctuation()); + add_class_and_color("js-object-element-index", palette.syntax_number()); + add_class_and_color("js-object-element-key", palette.syntax_string()); + + // FIXME: Add to palette? + add_class_and_color("js-error-name", Color::Red, true); + + return style.to_string(); +} + void ConsoleWidget::print_source_line(const StringView& source) { StringBuilder html; + html.append("<span class=\"repl-indicator\">"); html.append("> "); - // FIXME: Support output highlighting + html.append("</span>"); + + // FIXME: Support output source highlighting html.append(source); print_html(html.string_view()); @@ -132,7 +179,9 @@ void ConsoleWidget::print_value(JS::Value value, StringBuilder& output_html, Has // FIXME: Support output highlighting if (value.is_empty()) { + output_html.append("<span class=\"empty-object\">"); output_html.append("<empty>"); + output_html.append("</span>"); return; } @@ -140,7 +189,9 @@ void ConsoleWidget::print_value(JS::Value value, StringBuilder& output_html, Has if (seen_objects.contains(&value.as_object())) { // FIXME: Maybe we should only do this for circular references, // not for all reoccurring objects. + output_html.append("<span class=\"already-printed\">"); output_html.appendf("<already printed Object %p>", &value.as_object()); + output_html.append("</span>"); return; } seen_objects.set(&value.as_object()); @@ -161,67 +212,115 @@ void ConsoleWidget::print_value(JS::Value value, StringBuilder& output_html, Has } if (value.is_string()) + output_html.append("<span class=\"js-string\">"); + else if (value.is_number()) + output_html.append("<span class=\"js-number\">"); + else if (value.is_boolean()) + output_html.append("<span class=\"js-boolean\">"); + else if (value.is_null()) + output_html.append("<span class=\"js-null\">"); + else if (value.is_undefined()) + output_html.append("<span class=\"js-undefined\">"); + + if (value.is_string()) output_html.append('"'); output_html.append(value.to_string_without_side_effects()); if (value.is_string()) output_html.append('"'); + + output_html.append("</span>"); } void ConsoleWidget::print_array(const JS::Array& array, StringBuilder& html_output, HashTable<JS::Object*>& seen_objects) { + html_output.append("<span class=\"js-array-open\">"); html_output.append("[ "); + html_output.append("</span>"); for (size_t i = 0; i < array.elements().size(); ++i) { print_value(array.elements()[i], html_output, seen_objects); - if (i != array.elements().size() - 1) + if (i != array.elements().size() - 1) { + html_output.append("<span class=\"js-array-element-separator\">"); html_output.append(", "); + html_output.append("</span>"); + } } + html_output.append("<span class=\"js-array-close\">"); html_output.append(" ]"); + html_output.append("</span>"); } void ConsoleWidget::print_object(const JS::Object& object, StringBuilder& html_output, HashTable<JS::Object*>& seen_objects) { + html_output.append("<span class=\"js-object-open\">"); html_output.append("{ "); + html_output.append("</span>"); for (size_t i = 0; i < object.elements().size(); ++i) { if (object.elements()[i].is_empty()) continue; - html_output.appendf("\"m%zu\": ", i); + html_output.append("<span class=\"js-object-element-index\">"); + html_output.appendf("%zu", i); + html_output.append("</span>"); + html_output.append(": "); print_value(object.elements()[i], html_output, seen_objects); - if (i != object.elements().size() - 1) + if (i != object.elements().size() - 1) { + html_output.append("<span class=\"js-object-element-separator\">"); html_output.append(", "); + html_output.append("</span>"); + } } - if (!object.elements().is_empty() && object.shape().property_count()) + if (!object.elements().is_empty() && object.shape().property_count()) { + html_output.append("<span class=\"js-object-element-separator\">"); html_output.append(", "); + html_output.append("</span>"); + } size_t index = 0; for (auto& it : object.shape().property_table_ordered()) { - html_output.appendf("\"%s\": ", it.key.characters()); + html_output.append("<span class=\"js-object-element-key\">"); + html_output.appendf("\"%s\"", it.key.characters()); + html_output.append("</span>"); + html_output.append(": "); print_value(object.get_direct(it.value.offset), html_output, seen_objects); - if (index != object.shape().property_count() - 1) + if (index != object.shape().property_count() - 1) { + html_output.append("<span class=\"js-object-element-separator\">"); html_output.append(", "); + html_output.append("</span>"); + } ++index; } + html_output.append("<span class=\"js-object-close\">"); html_output.append(" }"); + html_output.append("</span>"); } void ConsoleWidget::print_function(const JS::Object& function, StringBuilder& html_output, HashTable<JS::Object*>&) { + html_output.append("<span class=\"js-function\">"); html_output.appendf("[%s]", function.class_name()); + html_output.append("</span>"); } void ConsoleWidget::print_date(const JS::Object& date, StringBuilder& html_output, HashTable<JS::Object*>&) { + html_output.append("<span class=\"js-date\">"); html_output.appendf("Date %s", static_cast<const JS::Date&>(date).string().characters()); + html_output.append("</span>"); } void ConsoleWidget::print_error(const JS::Object& object, StringBuilder& html_output, HashTable<JS::Object*>&) { auto& error = static_cast<const JS::Error&>(object); + html_output.append("<span class=\"js-error-name\">"); html_output.appendf("[%s]", error.name().characters()); - if (!error.message().is_empty()) + html_output.append("</span>"); + if (!error.message().is_empty()) { + html_output.append("<span class=\"js-error-message\">"); html_output.appendf(": %s", error.message().characters()); + html_output.append("</span>"); + } } void ConsoleWidget::print_html(const StringView& line) @@ -230,9 +329,10 @@ void ConsoleWidget::print_html(const StringView& line) paragraph->set_inner_html(line); m_console_output_container->append_child(paragraph); - m_console_output_container->set_needs_style_update(true); - m_console_output_container->document().schedule_style_update(); m_console_output_container->document().invalidate_layout(); + m_console_output_container->document().update_layout(); + + m_console_output_view->scroll_to_bottom(); } void ConsoleWidget::clear_output() diff --git a/Applications/Browser/ConsoleWidget.h b/Applications/Browser/ConsoleWidget.h index c6b2fb5811..5a0d0d7412 100644 --- a/Applications/Browser/ConsoleWidget.h +++ b/Applications/Browser/ConsoleWidget.h @@ -45,6 +45,8 @@ public: private: ConsoleWidget(); + String create_document_style(); + void print_value(JS::Value, StringBuilder& output_html, HashTable<JS::Object*> seen_objects = {}); void print_array(const JS::Array&, StringBuilder& output_html, HashTable<JS::Object*>&); void print_object(const JS::Object&, StringBuilder& output_html, HashTable<JS::Object*>&); |