ACAV f0ba4b7c9529
Abstract Syntax Tree (AST) visualization tool for C, C++, and Objective-C
Loading...
Searching...
No Matches
DeclContextView.cpp
1/*$!{
2* Aurora Clang AST Viewer (ACAV)
3*
4* Copyright (c) 2026 Min Liu
5* Copyright (c) 2026 Michael David Adams
6*
7* SPDX-License-Identifier: GPL-2.0-or-later
8*
9* This program is free software; you can redistribute it and/or modify
10* it under the terms of the GNU General Public License as published by
11* the Free Software Foundation; either version 2 of the License, or
12* (at your option) any later version.
13*
14* This program is distributed in the hope that it will be useful,
15* but WITHOUT ANY WARRANTY; without even the implied warranty of
16* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17* GNU General Public License for more details.
18*
19* You should have received a copy of the GNU General Public License along
20* with this program; if not, see <https://www.gnu.org/licenses/>.
21}$!*/
22
23#include "ui/DeclContextView.h"
25#include <QFrame>
26#include <QHeaderView>
27#include <QLabel>
28#include <QVBoxLayout>
29
30namespace acav {
31
32DeclContextView::DeclContextView(QWidget *parent) : QWidget(parent) {
33 setObjectName("declContextView");
34 setAttribute(Qt::WA_StyledBackground, true);
35 setupUI();
36}
37
38void DeclContextView::setupUI() {
39 auto *layout = new QVBoxLayout(this);
40 layout->setContentsMargins(4, 4, 4, 4);
41 layout->setSpacing(4);
42
43 // Semantic context
44 semanticLabel_ = new QLabel(tr("Semantic Context"), this);
45 layout->addWidget(semanticLabel_);
46
47 semanticTree_ = new QTreeWidget(this);
48 semanticTree_->setHeaderHidden(true);
49 semanticTree_->setRootIsDecorated(true);
50 semanticTree_->setAnimated(false); // Performance: disable animations
51 semanticTree_->setUniformRowHeights(true); // Performance: uniform row heights
52 semanticTree_->setIndentation(16);
53 semanticTree_->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
54 semanticTree_->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
55 semanticTree_->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
56 semanticTree_->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
57 semanticTree_->header()->setStretchLastSection(false);
58 semanticTree_->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
59 layout->addWidget(semanticTree_, 1);
60
61 // Separator between semantic and lexical contexts
62 auto *separator = new QFrame(this);
63 separator->setFrameShape(QFrame::HLine);
64 separator->setFrameShadow(QFrame::Sunken);
65 layout->addWidget(separator);
66
67 // Lexical context
68 lexicalLabel_ = new QLabel(tr("Lexical Context"), this);
69 layout->addWidget(lexicalLabel_);
70
71 lexicalTree_ = new QTreeWidget(this);
72 lexicalTree_->setHeaderHidden(true);
73 lexicalTree_->setRootIsDecorated(true);
74 lexicalTree_->setAnimated(false); // Performance: disable animations
75 lexicalTree_->setUniformRowHeights(true); // Performance: uniform row heights
76 lexicalTree_->setIndentation(16);
77 lexicalTree_->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
78 lexicalTree_->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
79 lexicalTree_->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
80 lexicalTree_->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
81 lexicalTree_->header()->setStretchLastSection(false);
82 lexicalTree_->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
83 layout->addWidget(lexicalTree_, 1);
84
85 // Connect signals for navigation (handles both mouse click and keyboard)
86 connect(semanticTree_, &QTreeWidget::currentItemChanged, this,
87 &DeclContextView::onContextItemChanged);
88 connect(lexicalTree_, &QTreeWidget::currentItemChanged, this,
89 &DeclContextView::onContextItemChanged);
90}
91
93 clear();
94
95 if (!node) {
96 return;
97 }
98
99 populateContextHierarchy(node);
100}
101
103 semanticTree_->clear();
104 lexicalTree_->clear();
105}
106
108 semanticTree_->setFocus();
109}
110
112 lexicalTree_->setFocus();
113}
114
115void DeclContextView::applyFont(const QFont &font) {
116 setFont(font);
117 semanticLabel_->setFont(font);
118 lexicalLabel_->setFont(font);
119 semanticTree_->setFont(font);
120 lexicalTree_->setFont(font);
121}
122
123void DeclContextView::populateContextHierarchy(AstViewNode *node) {
124 if (!node) {
125 return;
126 }
127
128 const auto &props = node->getProperties();
129
130 // Check if this is a Decl node (only Decl nodes have declaration contexts)
131 bool isDecl = props.contains("isDecl") && props.at("isDecl").is_boolean() &&
132 props.at("isDecl").get<bool>();
133
134 if (!isDecl) {
135 // Not a Decl node - show "Not applicable."
136 showNotApplicable();
137 return;
138 }
139
140 const AcavJson *semantic = nullptr;
141 const AcavJson *lexical = nullptr;
142
143 if (props.contains("semanticDeclContext") &&
144 props.at("semanticDeclContext").is_array()) {
145 semantic = &props.at("semanticDeclContext");
146 }
147 if (props.contains("lexicalDeclContext") &&
148 props.at("lexicalDeclContext").is_array()) {
149 lexical = &props.at("lexicalDeclContext");
150 }
151
152 populateContextTree(semanticTree_, semantic, true);
153 populateContextTree(lexicalTree_, lexical, true);
154
155 semanticTree_->expandAll();
156 lexicalTree_->expandAll();
157}
158
159void DeclContextView::showNotApplicable() {
160 auto *semanticItem = new QTreeWidgetItem({tr("Not applicable.")});
161 semanticItem->setForeground(0, QColor(128, 128, 128));
162 semanticTree_->addTopLevelItem(semanticItem);
163
164 auto *lexicalItem = new QTreeWidgetItem({tr("Not applicable.")});
165 lexicalItem->setForeground(0, QColor(128, 128, 128));
166 lexicalTree_->addTopLevelItem(lexicalItem);
167}
168
169void DeclContextView::populateContextTree(QTreeWidget *tree,
170 const AcavJson *contextArray,
171 bool highlightLast) {
172 if (!tree) {
173 return;
174 }
175 tree->clear();
176 if (!contextArray || contextArray->empty()) {
177 // No declaration context data - leave the tree empty
178 return;
179 }
180
181 QTreeWidgetItem *parentItem = nullptr;
182 const int count = static_cast<int>(contextArray->size());
183 for (int i = 0; i < count; ++i) {
184 const auto &entry = contextArray->at(i);
185 if (!entry.is_object()) {
186 continue;
187 }
188
189 QString kind = tr("(Unknown)");
190 QString name;
191 if (entry.contains("kind") && entry.at("kind").is_string()) {
192 kind = QString::fromStdString(
193 entry.at("kind").get<InternedString>().str());
194 }
195 if (entry.contains("name") && entry.at("name").is_string()) {
196 name = QString::fromStdString(
197 entry.at("name").get<InternedString>().str());
198 }
199
200 QString display = name.isEmpty() ? kind : QString("%1 %2").arg(kind, name);
201 auto *item = new QTreeWidgetItem({display});
202 applyKindStyling(item, kind);
203
204 // Store node pointer for navigation
205 if (entry.contains("nodePtr") && entry.at("nodePtr").is_number_unsigned()) {
206 auto ptr = entry.at("nodePtr").get<uint64_t>();
207 item->setData(0, Qt::UserRole, QVariant::fromValue(ptr));
208 }
209
210 const bool isSelected = highlightLast && i == count - 1;
211 if (isSelected) {
212 QFont font = item->font(0);
213 font.setBold(true);
214 item->setFont(0, font);
215 }
216
217 if (parentItem) {
218 parentItem->addChild(item);
219 } else {
220 tree->addTopLevelItem(item);
221 }
222
223 if (isSelected) {
224 tree->setCurrentItem(item);
225 item->setSelected(true);
226 }
227
228 parentItem = item;
229 }
230}
231
232void DeclContextView::onContextItemChanged(QTreeWidgetItem *current,
233 QTreeWidgetItem *previous) {
234 Q_UNUSED(previous);
235 if (!current) {
236 return;
237 }
238
239 QVariant data = current->data(0, Qt::UserRole);
240 if (!data.isValid()) {
241 return;
242 }
243
244 auto ptr = data.value<uint64_t>();
245 if (ptr == 0) {
246 return;
247 }
248
249 auto *node = reinterpret_cast<AstViewNode *>(ptr);
250 emit contextNodeClicked(node);
251}
252
253void DeclContextView::applyKindStyling(QTreeWidgetItem *item,
254 const QString &kind) const {
255 if (kind == "TranslationUnitDecl") {
256 item->setForeground(0, QColor(100, 100, 100)); // Gray
257 } else if (kind == "NamespaceDecl") {
258 item->setForeground(0, QColor(0, 100, 0)); // Dark green
259 } else if (kind.contains("Record") || kind.contains("Class")) {
260 item->setForeground(0, QColor(0, 0, 150)); // Blue
261 } else if (kind.contains("Function") || kind.contains("Method") ||
262 kind.contains("Constructor") || kind.contains("Destructor")) {
263 item->setForeground(0, QColor(150, 0, 150)); // Purple
264 } else if (kind == "EnumDecl") {
265 item->setForeground(0, QColor(150, 100, 0)); // Brown/orange
266 }
267}
268
269} // namespace acav
nlohmann::basic_json< std::map, std::vector, InternedString, bool, int64_t, uint64_t, double, std::allocator, nlohmann::adl_serializer, std::vector< uint8_t > > AcavJson
Custom JSON type using InternedString for automatic string deduplication.
Definition AstNode.h:51
Declaration context hierarchy display panel.
Memory-efficient immutable string with automatic deduplication.
Represents node in AST tree hierarchy.
Definition AstNode.h:195
const AcavJson & getProperties() const
Get node properties (delegates to data node).
Definition AstNode.h:217
void clear()
Clear the display.
void applyFont(const QFont &font)
Propagate font to all internal widgets (labels + trees).
void focusLexicalTree()
Set focus to lexical context tree.
void setSelectedNode(AstViewNode *node)
Update display for selected node.
void focusSemanticTree()
Set focus to semantic context tree.