Changeset View
Changeset View
Standalone View
Standalone View
plugins/clazy/utils.cpp
- This file was added.
1 | /* This file is part of KDevelop | ||||
---|---|---|---|---|---|
2 | | ||||
3 | Copyright 2018 Anton Anikin <anton@anikin.xyz> | ||||
4 | | ||||
5 | This program is free software; you can redistribute it and/or | ||||
6 | modify it under the terms of the GNU General Public | ||||
7 | License as published by the Free Software Foundation; either | ||||
8 | version 2 of the License, or (at your option) any later version. | ||||
9 | | ||||
10 | This program is distributed in the hope that it will be useful, | ||||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||||
13 | General Public License for more details. | ||||
14 | | ||||
15 | You should have received a copy of the GNU General Public License | ||||
16 | along with this program; see the file COPYING. If not, write to | ||||
17 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||||
18 | Boston, MA 02110-1301, USA. | ||||
19 | */ | ||||
20 | | ||||
21 | #include "utils.h" | ||||
22 | | ||||
23 | #include <interfaces/icore.h> | ||||
24 | #include <interfaces/iproject.h> | ||||
25 | #include <interfaces/iprojectcontroller.h> | ||||
26 | #include <klocalizedstring.h> | ||||
27 | #include <project/interfaces/ibuildsystemmanager.h> | ||||
28 | #include <project/projectmodel.h> | ||||
29 | | ||||
30 | #include <QFile> | ||||
31 | #include <QJsonDocument> | ||||
32 | #include <QJsonArray> | ||||
33 | #include <QJsonObject> | ||||
34 | #include <QRegularExpression> | ||||
35 | | ||||
36 | namespace Clazy | ||||
37 | { | ||||
38 | | ||||
39 | QString prettyPathName(const QString& path) | ||||
40 | { | ||||
41 | return KDevelop::ICore::self()->projectController()->prettyFileName( | ||||
42 | QUrl::fromLocalFile(path), | ||||
43 | KDevelop::IProjectController::FormatPlain); | ||||
44 | } | ||||
45 | | ||||
46 | QStringList compileCommandsFiles(const QString& jsonFilePath, QString& error) | ||||
47 | { | ||||
48 | QStringList paths; | ||||
49 | | ||||
50 | QFile jsonFile(jsonFilePath); | ||||
51 | if (!jsonFile.open(QFile::ReadOnly | QFile::Text)) { | ||||
52 | error = i18n("Unable to open %1 for reading", jsonFilePath); | ||||
53 | return paths; | ||||
54 | } | ||||
55 | | ||||
56 | QJsonParseError jsonError; | ||||
57 | auto document = QJsonDocument::fromJson(jsonFile.readAll(), &jsonError); | ||||
58 | | ||||
59 | if (jsonError.error) { | ||||
60 | error = i18n("JSON error during parsing %1: %2", jsonFilePath, jsonError.errorString()); | ||||
61 | return paths; | ||||
62 | } | ||||
63 | | ||||
64 | if (!document.isArray()) { | ||||
65 | error = i18n("JSON error during parsing %1: document is not an array", jsonFilePath); | ||||
66 | return paths; | ||||
67 | } | ||||
68 | | ||||
69 | static const QString KEY_FILE = QStringLiteral("file"); | ||||
70 | | ||||
71 | const auto array = document.array(); | ||||
kossebau: static is not really needed here, there is not anything special about this over the other… | |||||
72 | for (const auto& value : array) { | ||||
73 | if (!value.isObject()) { | ||||
74 | continue; | ||||
75 | } | ||||
76 | | ||||
77 | const QJsonObject entry = value.toObject(); | ||||
78 | if (entry.contains(KEY_FILE)) { | ||||
79 | auto path = entry[KEY_FILE].toString(); | ||||
80 | if (QFile::exists(path)) | ||||
81 | { | ||||
82 | paths += path; | ||||
83 | } | ||||
84 | } | ||||
85 | } | ||||
86 | | ||||
87 | return paths; | ||||
88 | } | ||||
89 | | ||||
90 | // Very simple Markdown parser/converter. Does not provide full Markdown language support and | ||||
91 | // was tested only with Clazy documentation. | ||||
92 | class MarkdownConverter | ||||
93 | { | ||||
94 | public: | ||||
FWIW: Okular uses the Discount library for reading markdown documents, showing them as HTML. Also Cantor will do so too: D14738: Add the markdown entry. Maybe it'd be a good idea to use it -- there are few options:
pino: FWIW: Okular uses the [[http://www.pell.portland.or.us/~orc/Code/discount/ | Discount library]]… | |||||
95 | MarkdownConverter() | ||||
96 | { | ||||
97 | tagStart.resize(STATE_COUNT); | ||||
98 | tagEnd.resize(STATE_COUNT); | ||||
99 | | ||||
100 | tagStart[EMPTY] = tagEnd[EMPTY] = QStringLiteral(""); | ||||
101 | | ||||
102 | tagStart[HEADING] = QStringLiteral("<b>"); | ||||
103 | tagEnd [HEADING] = QStringLiteral("</b>"); | ||||
104 | | ||||
105 | tagStart[PARAGRAPH] = QStringLiteral("<p>"); | ||||
106 | tagEnd [PARAGRAPH] = QStringLiteral("</p>"); | ||||
107 | | ||||
108 | tagStart[PREFORMATTED] = QStringLiteral("<pre>"); | ||||
109 | tagEnd [PREFORMATTED] = QStringLiteral("</pre>"); | ||||
110 | | ||||
111 | tagStart[LIST] = QStringLiteral("<ul><li>"); | ||||
112 | tagEnd [LIST] = QStringLiteral("</li></ul>"); | ||||
113 | } | ||||
114 | | ||||
115 | ~MarkdownConverter() | ||||
116 | { | ||||
117 | } | ||||
kossebau: = default | |||||
118 | | ||||
119 | QString toHtml(const QString& markdown) | ||||
120 | { | ||||
121 | const QRegularExpression hRE(QStringLiteral("(#+) (.+)")); | ||||
122 | QRegularExpressionMatch match; | ||||
123 | | ||||
124 | state = EMPTY; | ||||
125 | html.clear(); | ||||
126 | html += QStringLiteral("<html>"); | ||||
127 | | ||||
128 | auto lines = markdown.split('\n'); | ||||
129 | for (auto line : lines) { | ||||
130 | if (line.isEmpty()) { | ||||
131 | setState(EMPTY); | ||||
132 | continue; | ||||
133 | } | ||||
134 | | ||||
135 | if (line.startsWith("#")) { | ||||
136 | auto match = hRE.match(line); | ||||
137 | if (match.hasMatch()) { | ||||
138 | setState(HEADING); | ||||
139 | html += match.captured(2); | ||||
140 | setState(EMPTY); | ||||
141 | if (match.captured(1).size() == 1) { | ||||
142 | html += QStringLiteral("<hr>"); | ||||
143 | } | ||||
144 | } | ||||
145 | continue; | ||||
146 | } | ||||
147 | | ||||
148 | if (line.startsWith(QStringLiteral("```"))) { | ||||
149 | setState((state == PREFORMATTED) ? EMPTY : PREFORMATTED); | ||||
150 | continue; | ||||
151 | } | ||||
152 | | ||||
153 | if (line.startsWith(QStringLiteral(" "))) { | ||||
154 | if (state == EMPTY) { | ||||
155 | setState(PREFORMATTED); | ||||
156 | } | ||||
157 | } else if ( | ||||
158 | line.startsWith(QStringLiteral("- ")) || | ||||
159 | line.startsWith(QStringLiteral("* "))) { | ||||
160 | // force close and reopen list - this fixes cases when we don't have | ||||
161 | // separator line between items | ||||
162 | setState(EMPTY); | ||||
163 | setState(LIST); | ||||
164 | line = line.mid(2); | ||||
165 | } | ||||
166 | | ||||
167 | if (state == EMPTY) { | ||||
168 | setState(PARAGRAPH); | ||||
169 | } | ||||
170 | | ||||
171 | processLine(line); | ||||
172 | } | ||||
173 | setState(EMPTY); | ||||
174 | | ||||
175 | html += QStringLiteral("</html>"); | ||||
176 | return html.join('\n'); | ||||
177 | } | ||||
178 | | ||||
179 | protected: | ||||
180 | enum STATE { | ||||
181 | EMPTY, | ||||
182 | HEADING, | ||||
183 | PARAGRAPH, | ||||
184 | PREFORMATTED, | ||||
185 | LIST, | ||||
186 | | ||||
187 | STATE_COUNT | ||||
188 | }; | ||||
189 | | ||||
190 | void setState(int newState) | ||||
191 | { | ||||
192 | if (state == newState) { | ||||
193 | return; | ||||
194 | } | ||||
195 | | ||||
196 | if (state != EMPTY) { | ||||
197 | html += tagEnd[state]; | ||||
198 | } | ||||
199 | | ||||
200 | if (newState != EMPTY) { | ||||
201 | html += tagStart[newState]; | ||||
202 | } | ||||
203 | | ||||
204 | state = newState; | ||||
205 | } | ||||
206 | | ||||
207 | void processLine(QString& line) | ||||
208 | { | ||||
209 | static const QRegularExpression ttRE(QStringLiteral("`([^`]+)`")); | ||||
210 | static const QRegularExpression bdRE(QStringLiteral("\\*\\*([^\\*]+)\\*\\*")); | ||||
211 | static const QRegularExpression itRE(QStringLiteral("[^\\*]\\*([^\\*]+)\\*[^\\*]")); | ||||
212 | | ||||
213 | static auto applyRE = [](const QRegularExpression& re, QString& line, const QString& tag) { | ||||
214 | auto i = re.globalMatch(line); | ||||
215 | while (i.hasNext()) { | ||||
216 | auto match = i.next(); | ||||
217 | line.replace(match.captured(0), QStringLiteral("<%1>%2</%1>").arg(tag, match.captured(1))); | ||||
218 | } | ||||
219 | }; | ||||
220 | | ||||
221 | if (state != PREFORMATTED) { | ||||
222 | applyRE(ttRE, line, QStringLiteral("tt")); | ||||
223 | applyRE(bdRE, line, QStringLiteral("b")); | ||||
224 | applyRE(itRE, line, QStringLiteral("i")); | ||||
mini optimization: use QLatin1Char for single letter, using the replace(QChar, QLatin1String) overload kossebau: mini optimization: use QLatin1Char for single letter, using the `replace(QChar, QLatin1String)… | |||||
225 | } | ||||
226 | | ||||
227 | html += line; | ||||
228 | } | ||||
229 | | ||||
230 | int state; | ||||
231 | QVector<QString> tagStart; | ||||
232 | QVector<QString> tagEnd; | ||||
233 | QStringList html; | ||||
234 | }; | ||||
235 | | ||||
236 | QString markdown2html(const QByteArray& markdown) | ||||
237 | { | ||||
238 | MarkdownConverter converter; | ||||
239 | return converter.toHtml(markdown); | ||||
kossebau: split off data member with oen `private:` section | |||||
240 | } | ||||
241 | | ||||
242 | } |
static is not really needed here, there is not anything special about this over the other QStringLiterals?