summaryrefslogtreecommitdiff
path: root/DevTools/Inspector
diff options
context:
space:
mode:
authorAnotherTest <ali.mpfard@gmail.com>2020-06-30 23:03:53 +0430
committerAndreas Kling <kling@serenityos.org>2020-07-01 11:18:19 +0200
commit7b7200166712f2359120f69e7bfa5daa799564c6 (patch)
treea92e7d69d3cebaef5ce2a1f611bd9a7e294e2d44 /DevTools/Inspector
parent476ccb22069b8096273cf602c0d564bad24518eb (diff)
downloadserenity-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.cpp181
-rw-r--r--DevTools/Inspector/RemoteObjectPropertyModel.h15
-rw-r--r--DevTools/Inspector/main.cpp8
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);
};