1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "filecache.h"
#include <QtCore/qdebug.h>
#include <QtCore/qdir.h>
#include <QtCore/qfile.h>
#include <QtCore/qregularexpression.h>
#include <algorithm>
using namespace Qt::StringLiterals;
constexpr qsizetype MAX_CACHE_SIZE = 20;
static QString msgCannotFindSnippet(const QString &file, const QString &snippetLabel)
{
return "Cannot find snippet \""_L1 + snippetLabel + "\" in \""_L1
+ QDir::toNativeSeparators(file) + "\"."_L1;
}
static QString msgUnterminatedSnippet(const QString &file, const QString &snippetLabel)
{
return "Snippet \""_L1 + snippetLabel + "\" in \""_L1
+ QDir::toNativeSeparators(file) + "\" is not terminated."_L1;
}
static QString msgCannotOpenFileForReading(const QFile &f)
{
return "Failed to open file \""_L1 + QDir::toNativeSeparators(f.fileName())
+ "\" for reading: "_L1 +f.errorString();
}
std::optional<QString> FileCache::fileContents(const QString &name)
{
const qsizetype index = ensureEntry(name);
if (index == -1)
return std::nullopt;
return m_cache.at(index).contents;
}
void FileCache::ensureLines(FileCacheEntry *entry)
{
if (entry->lines.isEmpty())
entry->lines = QStringView{entry->contents}.split(u'\n');
}
std::optional<FileCache::Lines> FileCache::lines(const QString &name)
{
const qsizetype index = ensureEntry(name);
if (index == -1)
return std::nullopt;
FileCacheEntry &entry = m_cache[index];
ensureLines(&entry);
return entry.lines;
}
std::optional<QString> FileCache::fileSnippet(const QString &name,
const QString &snippetName,
const QRegularExpression &snippetPattern)
{
const qsizetype index = ensureEntry(name);
if (index == -1)
return std::nullopt;
FileCacheEntry &entry = m_cache[index];
ensureLines(&entry);
// Check for a comment line and the snippet ID
auto pred = [&snippetPattern](QStringView line) {
return (line.contains(u'/') || line.contains(u'#'))
&& snippetPattern.matchView(line).hasMatch(); };
const auto end = entry.lines.cend();
const auto i1 = std::find_if(entry.lines.cbegin(), end, pred);
if (i1 == end) {
m_error = msgCannotFindSnippet(name, snippetName);
return std::nullopt;
}
auto pos = i1;
const auto i2 = std::find_if(++pos, end, pred);
if (i2 == end) {
m_error = msgUnterminatedSnippet(name, snippetName);
return std::nullopt;
}
const QChar *startSnippet = i1->constData() + i1->size() + 1;
const auto snippetSize = i2->constData() - startSnippet;
const auto startSnippetIndex = startSnippet - entry.lines.cbegin()->constData();
return entry.contents.sliced(startSnippetIndex, snippetSize);
}
qsizetype FileCache::ensureEntry(const QString &name)
{
const qsizetype index = indexOf(name);
if (index != -1) {
++m_hits;
return index;
}
++m_misses;
m_error.clear();
QFile file(name);
if (!file.open(QIODevice::Text | QIODevice::ReadOnly)) {
m_error = msgCannotOpenFileForReading(file);
return -1;
}
QString contents = QString::fromUtf8(file.readAll());
m_cache.prepend({name, contents, {}});
while (m_cache.size() >= MAX_CACHE_SIZE)
m_cache.removeLast();
return 0;
}
qsizetype FileCache::indexOf(const QString &name) const
{
for (qsizetype i = 0, size = m_cache.size(); i < size; ++i) {
if (m_cache.at(i).name == name)
return i;
}
return -1;
}
void FileCache::formatDebug(QDebug &debug) const
{
debug << "FileCache(" << m_cache.size() << " entries, "
<< m_hits << " hits, " << m_misses << " misses [";
for (const auto &e : m_cache)
debug << QDir::toNativeSeparators(e.name) << ' ' << e.contents.size() << "B ";
debug << "])";
}
QDebug operator<<(QDebug debug, const FileCache &c)
{
QDebugStateSaver saver(debug);
debug.noquote();
debug.nospace();
c.formatDebug(debug);
return debug;
}
|