1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 04:27:44 +00:00

Spreadsheet: Display a detailed view of a cell error on hover

With this, Spreadsheet can now show an almost full stack trace for the
error (which is infintely better than just the error message).
This commit is contained in:
Ali Mohammad Pur 2022-06-26 00:12:23 +04:30 committed by Linus Groh
parent 64ef808aeb
commit db4a5aafc8
6 changed files with 62 additions and 1 deletions

View file

@ -50,6 +50,15 @@ struct Cell : public Weakable<Cell> {
bool dirty() const { return m_dirty; }
void clear_dirty() { m_dirty = false; }
StringView name_for_javascript(Sheet const& sheet) const
{
if (!m_name_for_javascript.is_empty())
return m_name_for_javascript;
m_name_for_javascript = String::formatted("cell {}", m_position.to_cell_identifier(sheet));
return m_name_for_javascript;
}
void set_thrown_value(JS::Value value) { m_thrown_value = value; }
Optional<JS::Value> thrown_value() const
{
@ -116,6 +125,7 @@ private:
CellType const* m_type { nullptr };
CellTypeMetadata m_type_metadata;
Position m_position;
mutable String m_name_for_javascript;
Vector<ConditionalFormat> m_conditional_formats;
Format m_evaluated_formats;

View file

@ -165,8 +165,12 @@ void Sheet::update(Cell& cell)
JS::ThrowCompletionOr<JS::Value> Sheet::evaluate(StringView source, Cell* on_behalf_of)
{
TemporaryChange cell_change { m_current_cell_being_evaluated, on_behalf_of };
auto name = on_behalf_of ? on_behalf_of->name_for_javascript(*this) : "cell <unknown>"sv;
auto script_or_error = JS::Script::parse(
source,
interpreter().realm(),
name);
auto script_or_error = JS::Script::parse(source, interpreter().realm());
if (script_or_error.is_error())
return interpreter().vm().throw_completion<JS::SyntaxError>(interpreter().global_object(), script_or_error.error().first().to_string());

View file

@ -103,6 +103,36 @@ GUI::Variant SheetModel::data(const GUI::ModelIndex& index, GUI::ModelRole role)
return {};
}
if (to_underlying(role) == to_underlying(Role::Tooltip)) {
auto const* cell = m_sheet->at({ (size_t)index.column(), (size_t)index.row() });
if (!cell || !cell->thrown_value().has_value())
return {};
auto value = cell->thrown_value().value();
if (!value.is_object())
return {};
auto& object = value.as_object();
if (!is<JS::Error>(object))
return {};
auto& error = static_cast<JS::Error&>(object);
auto const& trace = error.traceback();
StringBuilder builder;
builder.appendff("{}\n", error.get_without_side_effects(object.vm().names.message).to_string_without_side_effects());
for (auto const& frame : trace.in_reverse()) {
if (frame.source_range.filename.contains("runtime.js")) {
if (frame.function_name == "<unknown>")
builder.appendff(" in a builtin function at line {}, column {}\n", frame.source_range.start.line, frame.source_range.start.column);
else
builder.appendff(" while evaluating builtin '{}'\n", frame.function_name);
} else if (frame.source_range.filename.starts_with("cell ")) {
builder.appendff(" in cell '{}', at line {}, column {}\n", frame.source_range.filename.substring_view(5), frame.source_range.start.line, frame.source_range.start.column);
}
}
return builder.to_string();
}
return {};
}

View file

@ -13,6 +13,11 @@ namespace Spreadsheet {
class SheetModel final : public GUI::Model {
public:
enum class Role : UnderlyingType<GUI::ModelRole> {
_Custom = to_underlying(GUI::ModelRole::Custom),
Tooltip,
};
static NonnullRefPtr<SheetModel> create(Sheet& sheet) { return adopt_ref(*new SheetModel(sheet)); }
virtual ~SheetModel() override = default;

View file

@ -77,6 +77,16 @@ void InfinitelyScrollableTableView::mousemove_event(GUI::MouseEvent& event)
sheet.disable_updates();
ScopeGuard sheet_update_enabler { [&] { sheet.enable_updates(); } };
if (!is_dragging()) {
auto tooltip = model->data(index, static_cast<GUI::ModelRole>(SheetModel::Role::Tooltip));
if (tooltip.is_string()) {
set_tooltip(tooltip.as_string());
show_or_hide_tooltip();
} else {
set_tooltip({});
}
}
m_is_hovering_cut_zone = false;
m_is_hovering_extend_zone = false;
if (selection().size() > 0 && !m_is_dragging_for_select) {

View file

@ -78,6 +78,8 @@ private:
virtual void mouseup_event(GUI::MouseEvent&) override;
virtual void drop_event(GUI::DropEvent&) override;
bool is_dragging() const { return m_is_dragging_for_cut || m_is_dragging_for_extend || m_is_dragging_for_select; }
bool m_is_hovering_extend_zone { false };
bool m_is_hovering_cut_zone { false };
bool m_is_dragging_for_select { false };