diff options
author | AnotherTest <ali.mpfard@gmail.com> | 2020-06-30 23:03:53 +0430 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-07-01 11:18:19 +0200 |
commit | 7b7200166712f2359120f69e7bfa5daa799564c6 (patch) | |
tree | a92e7d69d3cebaef5ce2a1f611bd9a7e294e2d44 /DevTools/Inspector | |
parent | 476ccb22069b8096273cf602c0d564bad24518eb (diff) | |
download | serenity-7b7200166712f2359120f69e7bfa5daa799564c6.zip |
Inspector: Expand and show properties in a TreeView
This allows the inspector to show arbitrary json structures.
Diffstat (limited to 'DevTools/Inspector')
-rw-r--r-- | DevTools/Inspector/RemoteObjectPropertyModel.cpp | 181 | ||||
-rw-r--r-- | DevTools/Inspector/RemoteObjectPropertyModel.h | 15 | ||||
-rw-r--r-- | DevTools/Inspector/main.cpp | 8 |
3 files changed, 181 insertions, 23 deletions
diff --git a/DevTools/Inspector/RemoteObjectPropertyModel.cpp b/DevTools/Inspector/RemoteObjectPropertyModel.cpp index adb650235d..78f39bff05 100644 --- a/DevTools/Inspector/RemoteObjectPropertyModel.cpp +++ b/DevTools/Inspector/RemoteObjectPropertyModel.cpp @@ -33,9 +33,22 @@ RemoteObjectPropertyModel::RemoteObjectPropertyModel(RemoteObject& object) { } -int RemoteObjectPropertyModel::row_count(const GUI::ModelIndex&) const +int RemoteObjectPropertyModel::row_count(const GUI::ModelIndex& index) const { - return m_properties.size(); + Function<int(const JsonValue&)> do_count = [&](const JsonValue& value) { + if (value.is_array()) + return value.as_array().size(); + else if (value.is_object()) + return value.as_object().size(); + return 0; + }; + + if (index.is_valid()) { + auto* path = static_cast<const JsonPath*>(index.internal_data()); + return do_count(path->resolve(m_object.json)); + } else { + return do_count(m_object.json); + } } String RemoteObjectPropertyModel::column_name(int column) const @@ -51,13 +64,22 @@ String RemoteObjectPropertyModel::column_name(int column) const GUI::Variant RemoteObjectPropertyModel::data(const GUI::ModelIndex& index, Role role) const { - auto& property = m_properties[index.row()]; + auto* path = static_cast<const JsonPath*>(index.internal_data()); + if (!path) + return {}; + if (role == Role::Display) { switch (index.column()) { case Column::Name: - return property.name; - case Column::Value: - return property.value.to_string(); + return path->last().to_string(); + case Column::Value: { + auto data = path->resolve(m_object.json); + if (data.is_array()) + return String::format("<Array with %d element%s", data.as_array().size(), data.as_array().size() == 1 ? ">" : "s>"); + if (data.is_object()) + return String::format("<Object with %d entrie%s", data.as_object().size(), data.as_object().size() == 1 ? ">" : "s>"); + return data; + } } } return {}; @@ -65,18 +87,151 @@ GUI::Variant RemoteObjectPropertyModel::data(const GUI::ModelIndex& index, Role void RemoteObjectPropertyModel::update() { - m_properties.clear(); - m_object.json.for_each_member([this](auto& name, auto& value) { - m_properties.append({ name, value }); - }); did_update(); } void RemoteObjectPropertyModel::set_data(const GUI::ModelIndex& index, const GUI::Variant& new_value) { - auto& property = m_properties[index.row()]; + if (!index.is_valid()) + return; + + auto* path = static_cast<const JsonPath*>(index.internal_data()); + if (path->size() != 1) + return; + FlatPtr address = m_object.address; - RemoteProcess::the().set_property(address, property.name.to_string(), new_value.to_string()); - property.value = new_value.to_string(); + RemoteProcess::the().set_property(address, path->first().to_string(), new_value.to_string()); did_update(); } + +GUI::ModelIndex RemoteObjectPropertyModel::index(int row, int column, const GUI::ModelIndex& parent) const +{ + const auto& parent_path = parent.is_valid() ? *static_cast<const JsonPath*>(parent.internal_data()) : JsonPath {}; + + auto nth_child = [&](int n, const JsonValue& value) -> const JsonPath* { + auto path = make<JsonPath>(); + path->append(parent_path); + int row_index = n; + if (value.is_object()) { + String property_name; + auto& object = value.as_object(); + object.for_each_member([&](auto& name, auto&) { + if (row_index > 0) { + --row_index; + } else if (row_index == 0) { + property_name = name; + --row_index; + } + }); + if (property_name.is_null()) + return nullptr; + + path->append({ property_name }); + m_paths.append(move(path)); + } else if (value.is_array()) { + path->append(JsonPathElement { (size_t)n }); + m_paths.append(move(path)); + } else { + return nullptr; + } + return &m_paths.last(); + }; + + if (!parent.is_valid()) { + if (m_object.json.is_empty()) + return {}; + } + + auto index_path = cached_path_at(row, parent_path); + + if (!index_path) + index_path = nth_child(row, parent_path.resolve(m_object.json)); + + if (!index_path) + return {}; + + return create_index(row, column, index_path); +} + +GUI::ModelIndex RemoteObjectPropertyModel::parent_index(const GUI::ModelIndex& index) const +{ + if (!index.is_valid()) + return index; + + auto path = *static_cast<const JsonPath*>(index.internal_data()); + if (path.is_empty()) + return {}; + + path.take_last(); + if (path.is_empty()) + return {}; + + auto* cpath = find_cached_path(path); + if (cpath) { + int index_in_parent = 0; + if (cpath->last().kind() == JsonPathElement::Kind::Index) + index_in_parent = cpath->last().index(); + else if (cpath->last().kind() == JsonPathElement::Kind::Key) { + auto path_copy = path; + auto last = path_copy.take_last(); + bool found = false; + path_copy.resolve(m_object.json).as_object().for_each_member([&](auto& name, auto&) { + if (!found) { + if (last.key() == name) + found = true; + else + index_in_parent++; + } + }); + } + return create_index(index_in_parent, 0, cpath); + } + + dbg() << "No cached path found for path " << path.to_string(); + return {}; +} + +const JsonPath* RemoteObjectPropertyModel::cached_path_at(int n, const Vector<JsonPathElement>& prefix) const +{ + // FIXME: ModelIndex wants a void*, so we have to keep these + // indices alive, but allocating a new path every time + // we're asked for an index is silly, so we have to look for existing ones first. + const JsonPath* index_path = nullptr; + int row_index = n; + for (auto& path : m_paths) { + if (path.size() != prefix.size() + 1) + continue; + + for (size_t i = 0; i < prefix.size(); ++i) { + if (path[i] != prefix[i]) + goto do_continue; + } + + if (row_index == 0) { + index_path = &path; + break; + } + --row_index; + do_continue:; + } + + return index_path; +}; + +const JsonPath* RemoteObjectPropertyModel::find_cached_path(const Vector<JsonPathElement>& path) const +{ + for (auto& cpath : m_paths) { + if (cpath.size() != path.size()) + continue; + + for (size_t i = 0; i < cpath.size(); ++i) { + if (cpath[i] != path[i]) + goto do_continue; + } + + return &cpath; + do_continue:; + } + + return nullptr; +} diff --git a/DevTools/Inspector/RemoteObjectPropertyModel.h b/DevTools/Inspector/RemoteObjectPropertyModel.h index 377f386cec..36a01b32e6 100644 --- a/DevTools/Inspector/RemoteObjectPropertyModel.h +++ b/DevTools/Inspector/RemoteObjectPropertyModel.h @@ -26,14 +26,16 @@ #pragma once +#include <AK/JsonPath.h> #include <AK/JsonValue.h> +#include <AK/NonnullOwnPtrVector.h> #include <LibGUI/Model.h> class RemoteObject; class RemoteObjectPropertyModel final : public GUI::Model { public: - virtual ~RemoteObjectPropertyModel() override {} + virtual ~RemoteObjectPropertyModel() override { } static NonnullRefPtr<RemoteObjectPropertyModel> create(RemoteObject& object) { return adopt(*new RemoteObjectPropertyModel(object)); @@ -52,14 +54,15 @@ public: virtual void set_data(const GUI::ModelIndex&, const GUI::Variant&) override; virtual void update() override; virtual bool is_editable(const GUI::ModelIndex& index) const override { return index.column() == Column::Value; } + virtual GUI::ModelIndex index(int row, int column, const GUI::ModelIndex& parent = GUI::ModelIndex()) const override; + virtual GUI::ModelIndex parent_index(const GUI::ModelIndex&) const override; private: explicit RemoteObjectPropertyModel(RemoteObject&); + const JsonPath* cached_path_at(int n, const Vector<JsonPathElement>& prefix) const; + const JsonPath* find_cached_path(const Vector<JsonPathElement>& path) const; + RemoteObject& m_object; - struct NameAndValue { - JsonValue name; - JsonValue value; - }; - Vector<NameAndValue> m_properties; + mutable NonnullOwnPtrVector<JsonPath> m_paths; }; diff --git a/DevTools/Inspector/main.cpp b/DevTools/Inspector/main.cpp index 49cea918d9..ddded558db 100644 --- a/DevTools/Inspector/main.cpp +++ b/DevTools/Inspector/main.cpp @@ -94,15 +94,15 @@ int main(int argc, char** argv) tree_view.set_model(remote_process.object_graph_model()); tree_view.set_activates_on_selection(true); - auto& properties_table_view = splitter.add<GUI::TableView>(); - properties_table_view.set_editable(true); - properties_table_view.aid_create_editing_delegate = [](auto&) { + auto& properties_tree_view = splitter.add<GUI::TreeView>(); + properties_tree_view.set_editable(true); + properties_tree_view.aid_create_editing_delegate = [](auto&) { return make<GUI::StringModelEditingDelegate>(); }; tree_view.on_activation = [&](auto& index) { auto* remote_object = static_cast<RemoteObject*>(index.internal_data()); - properties_table_view.set_model(remote_object->property_model()); + properties_tree_view.set_model(remote_object->property_model()); remote_process.set_inspected_object(remote_object->address); }; |