ClassViewWidget.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. /*
  2. * Copyright (c) 2021, Itamar S. <itamar8910@gmail.com>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include "ClassViewWidget.h"
  27. #include "HackStudio.h"
  28. #include "ProjectDeclarations.h"
  29. #include <AK/BinarySearch.h>
  30. #include <AK/StdLibExtras.h>
  31. #include <LibGUI/BoxLayout.h>
  32. #include <string.h>
  33. namespace HackStudio {
  34. ClassViewWidget::ClassViewWidget()
  35. {
  36. set_layout<GUI::VerticalBoxLayout>();
  37. m_class_tree = add<GUI::TreeView>();
  38. m_class_tree->on_selection = [this](auto& index) {
  39. if (!index.is_valid())
  40. return;
  41. auto* node = static_cast<const ClassViewNode*>(index.internal_data());
  42. if (!node->declaration)
  43. return;
  44. open_file(node->declaration->position.file, node->declaration->position.line, node->declaration->position.column);
  45. };
  46. }
  47. RefPtr<ClassViewModel> ClassViewModel::create()
  48. {
  49. return adopt(*new ClassViewModel());
  50. }
  51. int ClassViewModel::row_count(const GUI::ModelIndex& index) const
  52. {
  53. if (!index.is_valid())
  54. return m_root_scope.size();
  55. auto* node = static_cast<ClassViewNode*>(index.internal_data());
  56. return node->children.size();
  57. }
  58. GUI::Variant ClassViewModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
  59. {
  60. auto* node = static_cast<const ClassViewNode*>(index.internal_data());
  61. switch (role) {
  62. case GUI::ModelRole::Display: {
  63. return node->name;
  64. }
  65. case GUI::ModelRole::Icon: {
  66. if (!node->declaration)
  67. return {};
  68. auto icon = ProjectDeclarations::get_icon_for(node->declaration->type);
  69. if (icon.has_value())
  70. return icon.value();
  71. return {};
  72. }
  73. default:
  74. return {};
  75. }
  76. }
  77. GUI::ModelIndex ClassViewModel::parent_index(const GUI::ModelIndex& index) const
  78. {
  79. if (!index.is_valid())
  80. return {};
  81. auto* child = static_cast<const ClassViewNode*>(index.internal_data());
  82. auto* parent = child->parent;
  83. if (parent == nullptr)
  84. return {};
  85. if (parent->parent == nullptr) {
  86. for (size_t row = 0; row < m_root_scope.size(); row++) {
  87. if (m_root_scope.ptr_at(row).ptr() == parent)
  88. return create_index(row, 0, parent);
  89. }
  90. VERIFY_NOT_REACHED();
  91. }
  92. for (size_t row = 0; row < parent->parent->children.size(); row++) {
  93. ClassViewNode* child_at_row = parent->parent->children.ptr_at(row).ptr();
  94. if (child_at_row == parent)
  95. return create_index(row, 0, parent);
  96. }
  97. VERIFY_NOT_REACHED();
  98. }
  99. GUI::ModelIndex ClassViewModel::index(int row, int column, const GUI::ModelIndex& parent_index) const
  100. {
  101. if (!parent_index.is_valid())
  102. return create_index(row, column, &m_root_scope[row]);
  103. auto* parent = static_cast<const ClassViewNode*>(parent_index.internal_data());
  104. auto* child = &parent->children[row];
  105. return create_index(row, column, child);
  106. }
  107. ClassViewModel::ClassViewModel()
  108. {
  109. m_root_scope.clear();
  110. ProjectDeclarations::the().for_each_declared_symbol([this](auto& decl) {
  111. if (decl.type == GUI::AutocompleteProvider::DeclarationType::Class
  112. || decl.type == GUI::AutocompleteProvider::DeclarationType::Struct
  113. || decl.type == GUI::AutocompleteProvider::DeclarationType::Member
  114. || decl.type == GUI::AutocompleteProvider::DeclarationType::Namespace) {
  115. add_declaration(decl);
  116. }
  117. });
  118. }
  119. static ClassViewNode& add_child_node(NonnullOwnPtrVector<ClassViewNode>& children, NonnullOwnPtr<ClassViewNode>&& node_ptr, ClassViewNode* parent, const GUI::AutocompleteProvider::Declaration* declaration)
  120. {
  121. node_ptr->parent = parent;
  122. node_ptr->declaration = declaration;
  123. size_t inserted_index = 0;
  124. ClassViewNode& node = *node_ptr;
  125. children.insert_before_matching(
  126. move(node_ptr), [&node](auto& other_node) {
  127. return strncmp(node.name.characters_without_null_termination(), other_node->name.characters_without_null_termination(), min(node.name.length(), other_node->name.length())) < 0;
  128. },
  129. 0, &inserted_index);
  130. return children.at(inserted_index);
  131. }
  132. void ClassViewModel::add_declaration(const GUI::AutocompleteProvider::Declaration& decl)
  133. {
  134. ClassViewNode* parent = nullptr;
  135. auto scope_parts = decl.scope.view().split_view("::");
  136. if (!scope_parts.is_empty()) {
  137. // Traverse declarations tree to the parent of 'decl'
  138. for (auto& node : m_root_scope) {
  139. if (node.name == scope_parts.first())
  140. parent = &node;
  141. }
  142. if (parent == nullptr) {
  143. m_root_scope.append(make<ClassViewNode>(scope_parts.first()));
  144. parent = &m_root_scope.last();
  145. }
  146. for (size_t i = 1; i < scope_parts.size(); ++i) {
  147. auto& scope = scope_parts[i];
  148. ClassViewNode* next { nullptr };
  149. for (auto& child : parent->children) {
  150. VERIFY(child.declaration);
  151. if (child.declaration->name == scope) {
  152. next = &child;
  153. break;
  154. }
  155. }
  156. if (next) {
  157. parent = next;
  158. continue;
  159. }
  160. parent = &add_child_node(parent->children, make<ClassViewNode>(scope), parent, nullptr);
  161. }
  162. }
  163. NonnullOwnPtrVector<ClassViewNode>* children_of_parent = nullptr;
  164. if (parent) {
  165. children_of_parent = &parent->children;
  166. } else {
  167. children_of_parent = &m_root_scope;
  168. }
  169. bool already_exists = false;
  170. for (auto& child : *children_of_parent) {
  171. if (child.name == decl.name) {
  172. already_exists = true;
  173. if (!child.declaration) {
  174. child.declaration = &decl;
  175. }
  176. break;
  177. }
  178. }
  179. if (!already_exists) {
  180. add_child_node(*children_of_parent, make<ClassViewNode>(decl.name), parent, &decl);
  181. }
  182. }
  183. void ClassViewWidget::refresh()
  184. {
  185. m_class_tree->set_model(ClassViewModel::create());
  186. }
  187. }