24#include <QCryptographicHash>
30#include <QFontDatabase>
31#include <QJsonDocument>
33#include <QStandardPaths>
40QString AppConfig::getDefaultConfigPath() {
42 QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
43 return homeDir +
"/.acav";
46void AppConfig::createDefaultConfigFile(
const QString &path) {
47 QFileInfo fileInfo(path);
48 QDir dir = fileInfo.dir();
54 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
55 qWarning(
"Failed to create default config file: %s",
60 QTextStream out(&file);
61 out <<
"# ACAV (Clang AST Viewer) Configuration File\n";
62 out <<
"# Location: ~/.acav\n";
63 out <<
"# Edit this file to customize application behavior\n";
64 out <<
"# Use File > Reload Configuration to apply changes\n";
67 out <<
"# Directory for storing AST files and dependency analysis results\n";
68 out <<
"# Use absolute path or leave empty to use ~/.cache/acav/\n";
69 out <<
"# Default: (empty, uses ~/.cache/acav/)\n";
73 out <<
"# Font family for UI (empty = system default)\n";
74 out <<
"# Example: fontFamily=Helvetica Neue\n";
75 out <<
"fontFamily=\n";
77 out <<
"# Font size for tree views and source code editor\n";
78 out <<
"# Valid range: 8-32\n";
79 out <<
"# Default: 11\n";
80 out <<
"fontSize=11\n";
83 out <<
"# Extract and display comments as properties on Decl nodes\n";
84 out <<
"# When enabled, documentation comments are stored in the 'comment' property\n";
85 out <<
"# Default: true\n";
86 out <<
"commentExtraction=true\n";
89 out <<
"# Enable memory profiling checkpoints\n";
90 out <<
"# Shows peak memory usage at key points during processing\n";
91 out <<
"# Default: false\n";
92 out <<
"enableMemoryProfiling=false\n";
94 out <<
"[performance]\n";
95 out <<
"# Number of CPU cores for parallel dependency analysis\n";
96 out <<
"# 0 = auto-detect (use all available cores)\n";
97 out <<
"# Default: 0\n";
98 out <<
"parallelProcessorCount=0\n";
108 QString configPath = configFilePath.isEmpty()
109 ? getDefaultConfigPath()
113 if (!QFile::exists(configPath)) {
114 createDefaultConfigFile(configPath);
117 instance_ =
new AppConfig(configPath);
122 qFatal(
"AppConfig::initialize() must be called before instance()");
127AppConfig::AppConfig(
const QString &configFilePath)
128 : configFilePath_(configFilePath),
129 settings_(configFilePath, QSettings::IniFormat) {
134 return configFilePath_;
138 if (!QFile::exists(configFilePath_)) {
139 createDefaultConfigFile(configFilePath_);
144 return QFile::exists(configFilePath_);
147void AppConfig::loadSettings() {
149 if (settings_.status() != QSettings::NoError) {
150 qWarning(
"Error reading config file '%s' (status=%d), using defaults",
151 qPrintable(configFilePath_),
static_cast<int>(settings_.status()));
156 cacheRoot_ = settings_.value(
"cache/root", QString()).toString();
157 commentExtractionEnabled_ =
158 settings_.value(
"ast/commentExtraction",
true).toBool();
159 memoryProfilingEnabled_ =
160 settings_.value(
"debug/enableMemoryProfiling",
false).toBool();
163 QString requestedFontFamily = settings_.value(
"ui/fontFamily", QString()).toString();
164 if (!requestedFontFamily.isEmpty()) {
165 QStringList availableFamilies = QFontDatabase::families();
166 if (availableFamilies.contains(requestedFontFamily, Qt::CaseInsensitive)) {
167 fontFamily_ = requestedFontFamily;
169 qWarning(
"Font family '%s' not found on system, using system default. "
170 "Available fonts can be listed with 'fc-list' command.",
171 qPrintable(requestedFontFamily));
179 constexpr int kDefaultFontSize = 11;
180 constexpr int kMinFontSize = 8;
181 constexpr int kMaxFontSize = 32;
182 fontSize_ = settings_.value(
"ui/fontSize", kDefaultFontSize).toInt();
183 if (fontSize_ < kMinFontSize || fontSize_ > kMaxFontSize) {
184 qWarning(
"Invalid fontSize=%d in config, using default %d (valid range: %d-%d)",
185 fontSize_, kDefaultFontSize, kMinFontSize, kMaxFontSize);
186 fontSize_ = kDefaultFontSize;
190 parallelProcessorCount_ = settings_.value(
"performance/parallelProcessorCount", 0).toInt();
191 if (parallelProcessorCount_ < 0) {
192 qWarning(
"Invalid parallelProcessorCount=%d in config, using 0 (auto-detect)",
193 parallelProcessorCount_);
194 parallelProcessorCount_ = 0;
198 QString defaultCacheRoot = QStandardPaths::writableLocation(QStandardPaths::HomeLocation)
200 if (cacheRoot_.isEmpty()) {
201 cacheRoot_ = defaultCacheRoot;
204 QDir dir(cacheRoot_);
207 if (!dir.mkpath(
".")) {
208 qWarning(
"Cannot create cache directory '%s', using default '%s'",
209 qPrintable(cacheRoot_), qPrintable(defaultCacheRoot));
210 cacheRoot_ = defaultCacheRoot;
214 QFile testFile(cacheRoot_ +
"/.acav_write_test");
215 if (testFile.open(QIODevice::WriteOnly)) {
219 qWarning(
"Cache directory '%s' is not writable, using default '%s'",
220 qPrintable(cacheRoot_), qPrintable(defaultCacheRoot));
221 cacheRoot_ = defaultCacheRoot;
232 return parallelProcessorCount_;
235QString AppConfig::hashPath(
const QString &path)
const {
237 QFileInfo fileInfo(path);
238 QString absolutePath = fileInfo.absoluteFilePath();
240 QCryptographicHash hash(QCryptographicHash::Sha256);
241 hash.addData(absolutePath.toUtf8());
242 QString hashStr = QString::fromLatin1(hash.result().toHex());
245 return hashStr.left(16);
252 QString pathHash = hashPath(compilationDatabasePath);
253 QString cacheDir = cacheRoot_ +
"/" + pathHash;
256 ensureCacheDirectory(compilationDatabasePath);
262 const QString &compilationDatabasePath)
const {
264 return cacheDir +
"/dependencies.json";
268 const QString &compilationDatabasePath)
const {
270 return cacheDir +
"/metadata.json";
274 const QString &compilationDatabasePath)
const {
276 QFile metadataFile(metadataPath);
278 if (!metadataFile.exists() ||
279 !metadataFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
281 QFileInfo info(compilationDatabasePath);
282 return info.absolutePath();
285 QByteArray data = metadataFile.readAll();
286 metadataFile.close();
288 QJsonDocument doc = QJsonDocument::fromJson(data);
289 if (doc.isNull() || !doc.isObject()) {
290 QFileInfo info(compilationDatabasePath);
291 return info.absolutePath();
294 QJsonObject metadata = doc.object();
295 return metadata[
"buildDirectory"].toString();
298void AppConfig::ensureCacheDirectory(
299 const QString &compilationDatabasePath)
const {
300 QString cacheDir = cacheRoot_ +
"/" + hashPath(compilationDatabasePath);
303 if (!dir.exists(cacheDir)) {
304 if (!dir.mkpath(cacheDir)) {
310 QString metadataPath = cacheDir +
"/metadata.json";
311 QFile metadataFile(metadataPath);
312 if (!metadataFile.exists()) {
313 if (metadataFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
314 QFileInfo info(compilationDatabasePath);
315 QJsonObject metadata;
316 metadata[
"compilationDatabasePath"] = info.absoluteFilePath();
317 metadata[
"buildDirectory"] = info.absolutePath();
318 metadata[
"createdAt"] = QDateTime::currentDateTime().toString(Qt::ISODate);
320 QJsonDocument doc(metadata);
321 metadataFile.write(doc.toJson(QJsonDocument::Indented));
322 metadataFile.close();
330 QString astDir = cacheDir +
"/ast";
334 if (!dir.exists(astDir)) {
342 const QString &sourceFilePath)
const {
344 QFileInfo sourceInfo(sourceFilePath);
345 QString fileName = sourceInfo.fileName();
347 return astDir +
"/" + fileName +
".ast";
351 return commentExtractionEnabled_;
355 return memoryProfilingEnabled_;
Application configuration and settings.
Application configuration manager.
bool reload()
Reload settings from the config file on disk.
QString getAstCacheDirectory(const QString &compilationDatabasePath) const
Get the AST cache directory for a compilation database.
QString getDependenciesFilePath(const QString &compilationDatabasePath) const
Get the dependencies JSON file path for a compilation database.
QString getCacheDirectory(const QString &compilationDatabasePath) const
Get the cache directory for a compilation database.
QString getMetadataFilePath(const QString &compilationDatabasePath) const
Get the metadata JSON file path for a compilation database.
QString getAstFilePath(const QString &compilationDatabasePath, const QString &sourceFilePath) const
Get the AST cache file path for a source file.
QString getCacheRoot() const
Custom cache root directory (default: ~/.cache/acav/).
QString getBuildDirectory(const QString &compilationDatabasePath) const
Get the build directory from cached metadata.
static AppConfig & instance()
Get the singleton instance.
static void initialize(const QString &configFilePath=QString())
Initialize the singleton with a custom config file path.
bool getCommentExtractionEnabled() const
Whether to extract and display comments in AST (default: false).
bool getMemoryProfilingEnabled() const
Whether memory profiling checkpoints are enabled (default: false).
QString getFontFamily() const
Editor/UI font family (default: empty = system default).
int getParallelProcessorCount() const
Number of parallel processors for query-dependencies (default: 0 = auto) 0 means use all available CP...
QString getConfigFilePath() const
Get the path to the currently loaded configuration file.
int getFontSize() const
Editor/UI font size (default: 11).