1 | // Copyright (C) 2021 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 |
3 | |
4 | #include "atom.h" |
5 | |
6 | #include "location.h" |
7 | #include "qdocdatabase.h" |
8 | |
9 | #include <QtCore/qregularexpression.h> |
10 | |
11 | #include <cstdio> |
12 | |
13 | QT_BEGIN_NAMESPACE |
14 | |
15 | /*! \class Atom |
16 | \brief The Atom class is the fundamental unit for representing |
17 | documents internally. |
18 | |
19 | Atoms have a \i type and are completed by a \i string whose |
20 | meaning depends on the \i type. For example, the string |
21 | \quotation |
22 | \i italic text looks nicer than \bold bold text |
23 | \endquotation |
24 | is represented by the following atoms: |
25 | \quotation |
26 | (FormattingLeft, ATOM_FORMATTING_ITALIC) |
27 | (String, "italic") |
28 | (FormattingRight, ATOM_FORMATTING_ITALIC) |
29 | (String, " text is more attractive than ") |
30 | (FormattingLeft, ATOM_FORMATTING_BOLD) |
31 | (String, "bold") |
32 | (FormattingRight, ATOM_FORMATTING_BOLD) |
33 | (String, " text") |
34 | \endquotation |
35 | |
36 | \also Text |
37 | */ |
38 | |
39 | /*! \enum Atom::AtomType |
40 | |
41 | \value AnnotatedList |
42 | \value AutoLink |
43 | \value BaseName |
44 | \value BriefLeft |
45 | \value BriefRight |
46 | \value C |
47 | \value CaptionLeft |
48 | \value CaptionRight |
49 | \value Code |
50 | \value CodeBad |
51 | \value CodeQuoteArgument |
52 | \value CodeQuoteCommand |
53 | \value DetailsLeft |
54 | \value DetailsRight |
55 | \value DivLeft |
56 | \value DivRight |
57 | \value ExampleFileLink |
58 | \value ExampleImageLink |
59 | \value FormatElse |
60 | \value FormatEndif |
61 | \value FormatIf |
62 | \value FootnoteLeft |
63 | \value FootnoteRight |
64 | \value FormattingLeft |
65 | \value FormattingRight |
66 | \value GeneratedList |
67 | \value Image |
68 | \value ImageText |
69 | \value ImportantNote |
70 | \value InlineImage |
71 | \value Keyword |
72 | \value LineBreak |
73 | \value Link |
74 | \value LinkNode |
75 | \value ListLeft |
76 | \value ListItemNumber |
77 | \value ListTagLeft |
78 | \value ListTagRight |
79 | \value ListItemLeft |
80 | \value ListItemRight |
81 | \value ListRight |
82 | \value NavAutoLink |
83 | \value NavLink |
84 | \value Nop |
85 | \value Note |
86 | \value ParaLeft |
87 | \value ParaRight |
88 | \value Qml |
89 | \value QuotationLeft |
90 | \value QuotationRight |
91 | \value RawString |
92 | \value SectionLeft |
93 | \value SectionRight |
94 | \value SectionHeadingLeft |
95 | \value SectionHeadingRight |
96 | \value SidebarLeft |
97 | \value SidebarRight |
98 | \value SinceList |
99 | \value SinceTagLeft |
100 | \value SinceTagRight |
101 | \value String |
102 | \value TableLeft |
103 | \value TableRight |
104 | \value TableHeaderLeft |
105 | \value TableHeaderRight |
106 | \value TableRowLeft |
107 | \value TableRowRight |
108 | \value TableItemLeft |
109 | \value TableItemRight |
110 | \value TableOfContents |
111 | \value Target |
112 | \value UnhandledFormat |
113 | \value UnknownCommand |
114 | */ |
115 | |
116 | QString Atom::s_noError = QString(); |
117 | |
118 | static const struct |
119 | { |
120 | const char *english; |
121 | int no; |
122 | } atms[] = { { .english: "AnnotatedList" , .no: Atom::AnnotatedList }, |
123 | { .english: "AutoLink" , .no: Atom::AutoLink }, |
124 | { .english: "BaseName" , .no: Atom::BaseName }, |
125 | { .english: "br" , .no: Atom::BR }, |
126 | { .english: "BriefLeft" , .no: Atom::BriefLeft }, |
127 | { .english: "BriefRight" , .no: Atom::BriefRight }, |
128 | { .english: "C" , .no: Atom::C }, |
129 | { .english: "CaptionLeft" , .no: Atom::CaptionLeft }, |
130 | { .english: "CaptionRight" , .no: Atom::CaptionRight }, |
131 | { .english: "Code" , .no: Atom::Code }, |
132 | { .english: "CodeBad" , .no: Atom::CodeBad }, |
133 | { .english: "CodeQuoteArgument" , .no: Atom::CodeQuoteArgument }, |
134 | { .english: "CodeQuoteCommand" , .no: Atom::CodeQuoteCommand }, |
135 | { .english: "DetailsLeft" , .no: Atom::DetailsLeft }, |
136 | { .english: "DetailsRight" , .no: Atom::DetailsRight }, |
137 | { .english: "DivLeft" , .no: Atom::DivLeft }, |
138 | { .english: "DivRight" , .no: Atom::DivRight }, |
139 | { .english: "ExampleFileLink" , .no: Atom::ExampleFileLink }, |
140 | { .english: "ExampleImageLink" , .no: Atom::ExampleImageLink }, |
141 | { .english: "FootnoteLeft" , .no: Atom::FootnoteLeft }, |
142 | { .english: "FootnoteRight" , .no: Atom::FootnoteRight }, |
143 | { .english: "FormatElse" , .no: Atom::FormatElse }, |
144 | { .english: "FormatEndif" , .no: Atom::FormatEndif }, |
145 | { .english: "FormatIf" , .no: Atom::FormatIf }, |
146 | { .english: "FormattingLeft" , .no: Atom::FormattingLeft }, |
147 | { .english: "FormattingRight" , .no: Atom::FormattingRight }, |
148 | { .english: "GeneratedList" , .no: Atom::GeneratedList }, |
149 | { .english: "hr" , .no: Atom::HR }, |
150 | { .english: "Image" , .no: Atom::Image }, |
151 | { .english: "ImageText" , .no: Atom::ImageText }, |
152 | { .english: "ImportantLeft" , .no: Atom::ImportantLeft }, |
153 | { .english: "ImportantRight" , .no: Atom::ImportantRight }, |
154 | { .english: "InlineImage" , .no: Atom::InlineImage }, |
155 | { .english: "Keyword" , .no: Atom::Keyword }, |
156 | { .english: "LegaleseLeft" , .no: Atom::LegaleseLeft }, |
157 | { .english: "LegaleseRight" , .no: Atom::LegaleseRight }, |
158 | { .english: "LineBreak" , .no: Atom::LineBreak }, |
159 | { .english: "Link" , .no: Atom::Link }, |
160 | { .english: "LinkNode" , .no: Atom::LinkNode }, |
161 | { .english: "ListLeft" , .no: Atom::ListLeft }, |
162 | { .english: "ListItemNumber" , .no: Atom::ListItemNumber }, |
163 | { .english: "ListTagLeft" , .no: Atom::ListTagLeft }, |
164 | { .english: "ListTagRight" , .no: Atom::ListTagRight }, |
165 | { .english: "ListItemLeft" , .no: Atom::ListItemLeft }, |
166 | { .english: "ListItemRight" , .no: Atom::ListItemRight }, |
167 | { .english: "ListRight" , .no: Atom::ListRight }, |
168 | { .english: "NavAutoLink" , .no: Atom::NavAutoLink }, |
169 | { .english: "NavLink" , .no: Atom::NavLink }, |
170 | { .english: "Nop" , .no: Atom::Nop }, |
171 | { .english: "NoteLeft" , .no: Atom::NoteLeft }, |
172 | { .english: "NoteRight" , .no: Atom::NoteRight }, |
173 | { .english: "ParaLeft" , .no: Atom::ParaLeft }, |
174 | { .english: "ParaRight" , .no: Atom::ParaRight }, |
175 | { .english: "Qml" , .no: Atom::Qml }, |
176 | { .english: "QuotationLeft" , .no: Atom::QuotationLeft }, |
177 | { .english: "QuotationRight" , .no: Atom::QuotationRight }, |
178 | { .english: "RawString" , .no: Atom::RawString }, |
179 | { .english: "SectionLeft" , .no: Atom::SectionLeft }, |
180 | { .english: "SectionRight" , .no: Atom::SectionRight }, |
181 | { .english: "SectionHeadingLeft" , .no: Atom::SectionHeadingLeft }, |
182 | { .english: "SectionHeadingRight" , .no: Atom::SectionHeadingRight }, |
183 | { .english: "SidebarLeft" , .no: Atom::SidebarLeft }, |
184 | { .english: "SidebarRight" , .no: Atom::SidebarRight }, |
185 | { .english: "SinceList" , .no: Atom::SinceList }, |
186 | { .english: "SinceTagLeft" , .no: Atom::SinceTagLeft }, |
187 | { .english: "SinceTagRight" , .no: Atom::SinceTagRight }, |
188 | { .english: "SnippetCommand" , .no: Atom::SnippetCommand }, |
189 | { .english: "SnippetIdentifier" , .no: Atom::SnippetIdentifier }, |
190 | { .english: "SnippetLocation" , .no: Atom::SnippetLocation }, |
191 | { .english: "String" , .no: Atom::String }, |
192 | { .english: "TableLeft" , .no: Atom::TableLeft }, |
193 | { .english: "TableRight" , .no: Atom::TableRight }, |
194 | { .english: "TableHeaderLeft" , .no: Atom::TableHeaderLeft }, |
195 | { .english: "TableHeaderRight" , .no: Atom::TableHeaderRight }, |
196 | { .english: "TableRowLeft" , .no: Atom::TableRowLeft }, |
197 | { .english: "TableRowRight" , .no: Atom::TableRowRight }, |
198 | { .english: "TableItemLeft" , .no: Atom::TableItemLeft }, |
199 | { .english: "TableItemRight" , .no: Atom::TableItemRight }, |
200 | { .english: "TableOfContents" , .no: Atom::TableOfContents }, |
201 | { .english: "Target" , .no: Atom::Target }, |
202 | { .english: "UnhandledFormat" , .no: Atom::UnhandledFormat }, |
203 | { .english: "WarningLeft" , .no: Atom::WarningLeft }, |
204 | { .english: "WarningRight" , .no: Atom::WarningRight }, |
205 | { .english: "UnknownCommand" , .no: Atom::UnknownCommand }, |
206 | { .english: nullptr, .no: 0 } }; |
207 | |
208 | /*! \fn Atom::Atom(AtomType type, const QString &string) |
209 | |
210 | Constructs an atom of the specified \a type with the single |
211 | parameter \a string and does not put the new atom in a list. |
212 | */ |
213 | |
214 | /*! \fn Atom::Atom(AtomType type, const QString &p1, const QString &p2) |
215 | |
216 | Constructs an atom of the specified \a type with the two |
217 | parameters \a p1 and \a p2 and does not put the new atom |
218 | in a list. |
219 | */ |
220 | |
221 | /*! \fn Atom(Atom *previous, AtomType type, const QString &string) |
222 | |
223 | Constructs an atom of the specified \a type with the single |
224 | parameter \a string and inserts the new atom into the list |
225 | after the \a previous atom. |
226 | */ |
227 | |
228 | /*! \fn Atom::Atom(Atom *previous, AtomType type, const QString &p1, const QString &p2) |
229 | |
230 | Constructs an atom of the specified \a type with the two |
231 | parameters \a p1 and \a p2 and inserts the new atom into |
232 | the list after the \a previous atom. |
233 | */ |
234 | |
235 | /*! \fn void Atom::appendChar(QChar ch) |
236 | |
237 | Appends \a ch to the string parameter of this atom. |
238 | |
239 | \also string() |
240 | */ |
241 | |
242 | /*! \fn void Atom::appendString(const QString &string) |
243 | |
244 | Appends \a string to the string parameter of this atom. |
245 | |
246 | \also string() |
247 | */ |
248 | |
249 | /*! \fn void Atom::chopString() |
250 | |
251 | \also string() |
252 | */ |
253 | |
254 | /*! \fn Atom *Atom::next() |
255 | Return the next atom in the atom list. |
256 | \also type(), string() |
257 | */ |
258 | |
259 | /*! |
260 | Return the next Atom in the list if it is of AtomType \a t. |
261 | Otherwise return 0. |
262 | */ |
263 | const Atom *Atom::next(AtomType t) const |
264 | { |
265 | return (m_next && (m_next->type() == t)) ? m_next : nullptr; |
266 | } |
267 | |
268 | /*! |
269 | Return the next Atom in the list if it is of AtomType \a t |
270 | and its string part is \a s. Otherwise return 0. |
271 | */ |
272 | const Atom *Atom::next(AtomType t, const QString &s) const |
273 | { |
274 | return (m_next && (m_next->type() == t) && (m_next->string() == s)) ? m_next : nullptr; |
275 | } |
276 | |
277 | /*! \fn const Atom *Atom::next() const |
278 | Return the next atom in the atom list. |
279 | \also type(), string() |
280 | */ |
281 | |
282 | /*! \fn AtomType Atom::type() const |
283 | Return the type of this atom. |
284 | \also string(), next() |
285 | */ |
286 | |
287 | /*! |
288 | Return the type of this atom as a string. Return "Invalid" if |
289 | type() returns an impossible value. |
290 | |
291 | This is only useful for debugging. |
292 | |
293 | \also type() |
294 | */ |
295 | QString Atom::typeString() const |
296 | { |
297 | static bool deja = false; |
298 | |
299 | if (!deja) { |
300 | int i = 0; |
301 | while (atms[i].english != nullptr) { |
302 | if (atms[i].no != i) |
303 | Location::internalError(QStringLiteral("QDoc::Atom: atom %1 missing" ).arg(a: i)); |
304 | ++i; |
305 | } |
306 | deja = true; |
307 | } |
308 | |
309 | int i = static_cast<int>(type()); |
310 | if (i < 0 || i > static_cast<int>(Last)) |
311 | return QLatin1String("Invalid" ); |
312 | return QLatin1String(atms[i].english); |
313 | } |
314 | |
315 | /*! \fn const QString &Atom::string() const |
316 | |
317 | Returns the string parameter that together with the type |
318 | characterizes this atom. |
319 | |
320 | \also type(), next() |
321 | */ |
322 | |
323 | /*! |
324 | For a link atom, returns the string representing the link text |
325 | if one exist in the list of atoms. |
326 | */ |
327 | QString Atom::linkText() const |
328 | { |
329 | Q_ASSERT(m_type == Atom::Link); |
330 | QString result; |
331 | |
332 | if (next() && next()->string() == ATOM_FORMATTING_LINK) { |
333 | auto *atom = next()->next(); |
334 | while (atom && atom->type() != Atom::FormattingRight) { |
335 | result += atom->string(); |
336 | atom = atom->next(); |
337 | } |
338 | return result; |
339 | } |
340 | |
341 | return string(); |
342 | } |
343 | |
344 | /*! |
345 | The only constructor for LinkAtom. It creates an Atom of |
346 | type Atom::Link. \a p1 being the link target. \a p2 is the |
347 | parameters in square brackets. Normally there is just one |
348 | word in the square brackets, but there can be up to three |
349 | words separated by spaces. The constructor splits \a p2 on |
350 | the space character. |
351 | */ |
352 | LinkAtom::LinkAtom(const QString &p1, const QString &p2) |
353 | : Atom(Atom::Link, p1), |
354 | m_resolved(false), |
355 | m_genus(Node::DontCare), |
356 | m_domain(nullptr), |
357 | m_squareBracketParams(p2) |
358 | { |
359 | // nada. |
360 | } |
361 | |
362 | /*! |
363 | This function resolves the parameters that were enclosed in |
364 | square brackets. If the parameters have already been resolved, |
365 | it does nothing and returns immediately. |
366 | */ |
367 | void LinkAtom::resolveSquareBracketParams() |
368 | { |
369 | if (m_resolved) |
370 | return; |
371 | const QStringList params = m_squareBracketParams.toLower().split(sep: QLatin1Char(' ')); |
372 | for (const auto ¶m : params) { |
373 | if (!m_domain) { |
374 | m_domain = QDocDatabase::qdocDB()->findTree(t: param); |
375 | if (m_domain) { |
376 | continue; |
377 | } |
378 | } |
379 | |
380 | if (param == "qml" ) { |
381 | m_genus = Node::QML; |
382 | continue; |
383 | } |
384 | if (param == "cpp" ) { |
385 | m_genus = Node::CPP; |
386 | continue; |
387 | } |
388 | if (param == "doc" ) { |
389 | m_genus = Node::DOC; |
390 | continue; |
391 | } |
392 | if (param == "api" ) { |
393 | m_genus = Node::API; |
394 | continue; |
395 | } |
396 | m_error = m_squareBracketParams; |
397 | break; |
398 | } |
399 | m_resolved = true; |
400 | } |
401 | |
402 | /*! |
403 | Standard copy constructor of LinkAtom \a t. |
404 | */ |
405 | LinkAtom::LinkAtom(const LinkAtom &t) |
406 | : Atom(Link, t.string()), |
407 | m_resolved(t.m_resolved), |
408 | m_genus(t.m_genus), |
409 | m_domain(t.m_domain), |
410 | m_error(t.m_error), |
411 | m_squareBracketParams(t.m_squareBracketParams) |
412 | { |
413 | // nothing |
414 | } |
415 | |
416 | /*! |
417 | Special copy constructor of LinkAtom \a t, where |
418 | where the new LinkAtom will not be the first one |
419 | in the list. |
420 | */ |
421 | LinkAtom::LinkAtom(Atom *previous, const LinkAtom &t) |
422 | : Atom(previous, Link, t.string()), |
423 | m_resolved(t.m_resolved), |
424 | m_genus(t.m_genus), |
425 | m_domain(t.m_domain), |
426 | m_error(t.m_error), |
427 | m_squareBracketParams(t.m_squareBracketParams) |
428 | { |
429 | previous->m_next = this; |
430 | } |
431 | |
432 | QT_END_NAMESPACE |
433 | |