Serenity Operating System
at master 158 lines 4.4 kB view raw
1/* 2 * Copyright (c) 2020, Hunter Salyer <thefalsehonesty@gmail.com> 3 * Copyright (c) 2021-2022, Andreas Kling <kling@serenityos.org> 4 * Copyright (c) 2021, Sam Atkins <atkinssj@serenityos.org> 5 * Copyright (c) 2022, the SerenityOS developers. 6 * 7 * SPDX-License-Identifier: BSD-2-Clause 8 */ 9 10#define AK_DONT_REPLACE_STD 11 12#include "ConsoleWidget.h" 13#include "Utilities.h" 14#include <AK/StringBuilder.h> 15#include <LibJS/MarkupGenerator.h> 16#include <QLineEdit> 17#include <QPushButton> 18#include <QTextEdit> 19#include <QVBoxLayout> 20 21namespace Ladybird { 22 23ConsoleWidget::ConsoleWidget() 24{ 25 setLayout(new QVBoxLayout); 26 27 m_output_view = new QTextEdit(this); 28 m_output_view->setReadOnly(true); 29 layout()->addWidget(m_output_view); 30 31 if (on_request_messages) 32 on_request_messages(0); 33 34 auto* bottom_container = new QWidget(this); 35 bottom_container->setLayout(new QHBoxLayout); 36 37 layout()->addWidget(bottom_container); 38 39 m_input = new QLineEdit(bottom_container); 40 bottom_container->layout()->addWidget(m_input); 41 42 QObject::connect(m_input, &QLineEdit::returnPressed, [this] { 43 auto js_source = ak_deprecated_string_from_qstring(m_input->text()); 44 45 if (js_source.is_whitespace()) 46 return; 47 48 m_input->clear(); 49 50 print_source_line(js_source); 51 52 if (on_js_input) 53 on_js_input(js_source); 54 }); 55 56 setFocusProxy(m_input); 57 58 auto* clear_button = new QPushButton(bottom_container); 59 bottom_container->layout()->addWidget(clear_button); 60 clear_button->setFixedSize(22, 22); 61 clear_button->setText("X"); 62 clear_button->setToolTip("Clear the console output"); 63 QObject::connect(clear_button, &QPushButton::pressed, [this] { 64 clear_output(); 65 }); 66 67 m_input->setFocus(); 68} 69 70void ConsoleWidget::request_console_messages() 71{ 72 VERIFY(!m_waiting_for_messages); 73 VERIFY(on_request_messages); 74 on_request_messages(m_highest_received_message_index + 1); 75 m_waiting_for_messages = true; 76} 77 78void ConsoleWidget::notify_about_new_console_message(i32 message_index) 79{ 80 if (message_index <= m_highest_received_message_index) { 81 dbgln("Notified about console message we already have"); 82 return; 83 } 84 if (message_index <= m_highest_notified_message_index) { 85 dbgln("Notified about console message we're already aware of"); 86 return; 87 } 88 89 m_highest_notified_message_index = message_index; 90 if (!m_waiting_for_messages) 91 request_console_messages(); 92} 93 94void ConsoleWidget::handle_console_messages(i32 start_index, Vector<DeprecatedString> const& message_types, Vector<DeprecatedString> const& messages) 95{ 96 i32 end_index = start_index + message_types.size() - 1; 97 if (end_index <= m_highest_received_message_index) { 98 dbgln("Received old console messages"); 99 return; 100 } 101 102 for (size_t i = 0; i < message_types.size(); i++) { 103 auto& type = message_types[i]; 104 auto& message = messages[i]; 105 106 if (type == "html") { 107 print_html(message); 108 } else if (type == "clear") { 109 clear_output(); 110 } else if (type == "group") { 111 // FIXME: Implement. 112 } else if (type == "groupCollapsed") { 113 // FIXME: Implement. 114 } else if (type == "groupEnd") { 115 // FIXME: Implement. 116 } else { 117 VERIFY_NOT_REACHED(); 118 } 119 } 120 121 m_highest_received_message_index = end_index; 122 m_waiting_for_messages = false; 123 124 if (m_highest_received_message_index < m_highest_notified_message_index) 125 request_console_messages(); 126} 127 128void ConsoleWidget::print_source_line(StringView source) 129{ 130 StringBuilder html; 131 html.append("<span class=\"repl-indicator\">"sv); 132 html.append("&gt; "sv); 133 html.append("</span>"sv); 134 135 html.append(JS::MarkupGenerator::html_from_source(source).release_value_but_fixme_should_propagate_errors()); 136 137 print_html(html.string_view()); 138} 139 140void ConsoleWidget::print_html(StringView line) 141{ 142 m_output_view->append(QString::fromUtf8(line.characters_without_null_termination(), line.length())); 143} 144 145void ConsoleWidget::clear_output() 146{ 147 m_output_view->clear(); 148} 149 150void ConsoleWidget::reset() 151{ 152 clear_output(); 153 m_highest_notified_message_index = -1; 154 m_highest_received_message_index = -1; 155 m_waiting_for_messages = false; 156} 157 158}