Warning: That file was not part of the compilation database. It may have many parsing errors.
1 | /**************************************************************************** |
---|---|
2 | ** |
3 | ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the tools applications of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and Digia. For licensing terms and |
14 | ** conditions see http://qt.digia.com/licensing. For further information |
15 | ** use the contact form at http://qt.digia.com/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 2.1 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 2.1 requirements |
23 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
24 | ** |
25 | ** In addition, as a special exception, Digia gives you certain additional |
26 | ** rights. These rights are described in the Digia Qt LGPL Exception |
27 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
28 | ** |
29 | ** GNU General Public License Usage |
30 | ** Alternatively, this file may be used under the terms of the GNU |
31 | ** General Public License version 3.0 as published by the Free Software |
32 | ** Foundation and appearing in the file LICENSE.GPL included in the |
33 | ** packaging of this file. Please review the following information to |
34 | ** ensure the GNU General Public License version 3.0 requirements will be |
35 | ** met: http://www.gnu.org/copyleft/gpl.html. |
36 | ** |
37 | ** |
38 | ** $QT_END_LICENSE$ |
39 | ** |
40 | ****************************************************************************/ |
41 | |
42 | #include "config.h" |
43 | #include "doc.h" |
44 | #include "codemarker.h" |
45 | #include "editdistance.h" |
46 | #include "openedlist.h" |
47 | #include "quoter.h" |
48 | #include "text.h" |
49 | #include "tokenizer.h" |
50 | #include <qdatetime.h> |
51 | #include <qfile.h> |
52 | #include <qfileinfo.h> |
53 | #include <qhash.h> |
54 | #include <qtextstream.h> |
55 | #include <qregexp.h> |
56 | #include <ctype.h> |
57 | #include <limits.h> |
58 | #include <qdebug.h> |
59 | |
60 | QT_BEGIN_NAMESPACE |
61 | |
62 | Q_GLOBAL_STATIC(QSet<QString>, null_Set_QString) |
63 | Q_GLOBAL_STATIC(QStringList, null_QStringList) |
64 | Q_GLOBAL_STATIC(QList<Text>, null_QList_Text) |
65 | //Q_GLOBAL_STATIC(QStringMap, null_QStringMap) |
66 | Q_GLOBAL_STATIC(QStringMultiMap, null_QStringMultiMap) |
67 | |
68 | struct Macro |
69 | { |
70 | QString defaultDef; |
71 | Location defaultDefLocation; |
72 | QStringMap otherDefs; |
73 | int numParams; |
74 | }; |
75 | |
76 | enum { |
77 | CMD_A, |
78 | CMD_ABSTRACT, |
79 | CMD_ANNOTATEDLIST, |
80 | CMD_BADCODE, |
81 | CMD_BASENAME, |
82 | CMD_BOLD, |
83 | CMD_BRIEF, |
84 | CMD_C, |
85 | CMD_CAPTION, |
86 | CMD_CHAPTER, // 9 |
87 | CMD_CODE, |
88 | CMD_CODELINE, |
89 | CMD_DIV, |
90 | CMD_DOTS, |
91 | CMD_ELSE, |
92 | CMD_ENDABSTRACT, |
93 | CMD_ENDCHAPTER, |
94 | CMD_ENDCODE, |
95 | CMD_ENDDIV, |
96 | CMD_ENDFOOTNOTE, |
97 | CMD_ENDIF, |
98 | CMD_ENDLEGALESE, |
99 | CMD_ENDLINK, |
100 | CMD_ENDLIST, |
101 | CMD_ENDOMIT, |
102 | CMD_ENDPART, |
103 | CMD_ENDQUOTATION, |
104 | CMD_ENDRAW, |
105 | CMD_ENDSECTION1, |
106 | CMD_ENDSECTION2, |
107 | CMD_ENDSECTION3, |
108 | CMD_ENDSECTION4, |
109 | CMD_ENDSIDEBAR, |
110 | CMD_ENDTABLE, |
111 | CMD_EXPIRE, |
112 | CMD_FOOTNOTE, |
113 | CMD_GENERATELIST, |
114 | CMD_GRANULARITY, |
115 | CMD_HEADER, |
116 | CMD_I, |
117 | CMD_IF, |
118 | CMD_IMAGE, |
119 | CMD_INCLUDE, |
120 | CMD_INLINEIMAGE, |
121 | CMD_INDEX, |
122 | CMD_KEYWORD, |
123 | CMD_L, |
124 | CMD_LEGALESE, |
125 | CMD_LINK, |
126 | CMD_LIST, |
127 | CMD_META, |
128 | CMD_NEWCODE, |
129 | CMD_O, |
130 | CMD_OLDCODE, |
131 | CMD_OMIT, |
132 | CMD_OMITVALUE, |
133 | CMD_OVERLOAD, |
134 | CMD_PART, |
135 | CMD_PRINTLINE, |
136 | CMD_PRINTTO, |
137 | CMD_PRINTUNTIL, |
138 | CMD_QUOTATION, |
139 | CMD_QUOTEFILE, |
140 | CMD_QUOTEFROMFILE, |
141 | CMD_QUOTEFUNCTION, |
142 | CMD_RAW, |
143 | CMD_ROW, |
144 | CMD_SA, |
145 | CMD_SECTION1, // 68 |
146 | CMD_SECTION2, // 69 |
147 | CMD_SECTION3, // 70 |
148 | CMD_SECTION4, // 71 |
149 | CMD_SIDEBAR, |
150 | CMD_SINCELIST, |
151 | CMD_SKIPLINE, |
152 | CMD_SKIPTO, |
153 | CMD_SKIPUNTIL, |
154 | CMD_SNIPPET, |
155 | CMD_SPAN, |
156 | CMD_SUB, |
157 | CMD_SUP, |
158 | CMD_TABLE, |
159 | CMD_TABLEOFCONTENTS, |
160 | CMD_TARGET, |
161 | CMD_TT, |
162 | CMD_UNDERLINE, |
163 | CMD_UNICODE, |
164 | CMD_VALUE, |
165 | CMD_WARNING, |
166 | CMD_QML, |
167 | CMD_ENDQML, |
168 | CMD_CPP, |
169 | CMD_ENDCPP, |
170 | CMD_QMLTEXT, |
171 | CMD_ENDQMLTEXT, |
172 | CMD_CPPTEXT, |
173 | CMD_ENDCPPTEXT, |
174 | CMD_JS, |
175 | CMD_ENDJS, |
176 | NOT_A_CMD |
177 | }; |
178 | |
179 | static struct { |
180 | const char *english; |
181 | int no; |
182 | QString *alias; |
183 | } cmds[] = { |
184 | { "a", CMD_A, 0 }, |
185 | { "abstract", CMD_ABSTRACT, 0 }, |
186 | { "annotatedlist", CMD_ANNOTATEDLIST, 0 }, |
187 | { "badcode", CMD_BADCODE, 0 }, |
188 | { "basename", CMD_BASENAME, 0 }, // ### don't document for now |
189 | { "bold", CMD_BOLD, 0 }, |
190 | { "brief", CMD_BRIEF, 0 }, |
191 | { "c", CMD_C, 0 }, |
192 | { "caption", CMD_CAPTION, 0 }, |
193 | { "chapter", CMD_CHAPTER, 0 }, |
194 | { "code", CMD_CODE, 0 }, |
195 | { "codeline", CMD_CODELINE, 0}, |
196 | { "div", CMD_DIV, 0 }, |
197 | { "dots", CMD_DOTS, 0 }, |
198 | { "else", CMD_ELSE, 0 }, |
199 | { "endabstract", CMD_ENDABSTRACT, 0 }, |
200 | { "endchapter", CMD_ENDCHAPTER, 0 }, |
201 | { "endcode", CMD_ENDCODE, 0 }, |
202 | { "enddiv", CMD_ENDDIV, 0 }, |
203 | { "endfootnote", CMD_ENDFOOTNOTE, 0 }, |
204 | { "endif", CMD_ENDIF, 0 }, |
205 | { "endlegalese", CMD_ENDLEGALESE, 0 }, |
206 | { "endlink", CMD_ENDLINK, 0 }, |
207 | { "endlist", CMD_ENDLIST, 0 }, |
208 | { "endomit", CMD_ENDOMIT, 0 }, |
209 | { "endpart", CMD_ENDPART, 0 }, |
210 | { "endquotation", CMD_ENDQUOTATION, 0 }, |
211 | { "endraw", CMD_ENDRAW, 0 }, |
212 | { "endsection1", CMD_ENDSECTION1, 0 }, // ### don't document for now |
213 | { "endsection2", CMD_ENDSECTION2, 0 }, // ### don't document for now |
214 | { "endsection3", CMD_ENDSECTION3, 0 }, // ### don't document for now |
215 | { "endsection4", CMD_ENDSECTION4, 0 }, // ### don't document for now |
216 | { "endsidebar", CMD_ENDSIDEBAR, 0 }, |
217 | { "endtable", CMD_ENDTABLE, 0 }, |
218 | { "expire", CMD_EXPIRE, 0 }, |
219 | { "footnote", CMD_FOOTNOTE, 0 }, |
220 | { "generatelist", CMD_GENERATELIST, 0 }, |
221 | { "granularity", CMD_GRANULARITY, 0 }, // ### don't document for now |
222 | { "header", CMD_HEADER, 0 }, |
223 | { "i", CMD_I, 0 }, |
224 | { "if", CMD_IF, 0 }, |
225 | { "image", CMD_IMAGE, 0 }, |
226 | { "include", CMD_INCLUDE, 0 }, |
227 | { "inlineimage", CMD_INLINEIMAGE, 0 }, |
228 | { "index", CMD_INDEX, 0 }, // ### don't document for now |
229 | { "keyword", CMD_KEYWORD, 0 }, |
230 | { "l", CMD_L, 0 }, |
231 | { "legalese", CMD_LEGALESE, 0 }, |
232 | { "link", CMD_LINK, 0 }, |
233 | { "list", CMD_LIST, 0 }, |
234 | { "meta", CMD_META, 0 }, |
235 | { "newcode", CMD_NEWCODE, 0 }, |
236 | { "o", CMD_O, 0 }, |
237 | { "oldcode", CMD_OLDCODE, 0 }, |
238 | { "omit", CMD_OMIT, 0 }, |
239 | { "omitvalue", CMD_OMITVALUE, 0 }, |
240 | { "overload", CMD_OVERLOAD, 0 }, |
241 | { "part", CMD_PART, 0 }, |
242 | { "printline", CMD_PRINTLINE, 0 }, |
243 | { "printto", CMD_PRINTTO, 0 }, |
244 | { "printuntil", CMD_PRINTUNTIL, 0 }, |
245 | { "quotation", CMD_QUOTATION, 0 }, |
246 | { "quotefile", CMD_QUOTEFILE, 0 }, |
247 | { "quotefromfile", CMD_QUOTEFROMFILE, 0 }, |
248 | { "quotefunction", CMD_QUOTEFUNCTION, 0 }, // ### don't document for now |
249 | { "raw", CMD_RAW, 0 }, |
250 | { "row", CMD_ROW, 0 }, |
251 | { "sa", CMD_SA, 0 }, |
252 | { "section1", CMD_SECTION1, 0 }, |
253 | { "section2", CMD_SECTION2, 0 }, |
254 | { "section3", CMD_SECTION3, 0 }, |
255 | { "section4", CMD_SECTION4, 0 }, |
256 | { "sidebar", CMD_SIDEBAR, 0 }, // ### don't document for now |
257 | { "sincelist", CMD_SINCELIST, 0 }, |
258 | { "skipline", CMD_SKIPLINE, 0 }, |
259 | { "skipto", CMD_SKIPTO, 0 }, |
260 | { "skipuntil", CMD_SKIPUNTIL, 0 }, |
261 | { "snippet", CMD_SNIPPET, 0 }, |
262 | { "span", CMD_SPAN, 0 }, |
263 | { "sub", CMD_SUB, 0 }, |
264 | { "sup", CMD_SUP, 0 }, |
265 | { "table", CMD_TABLE, 0 }, |
266 | { "tableofcontents", CMD_TABLEOFCONTENTS, 0 }, |
267 | { "target", CMD_TARGET, 0 }, |
268 | { "tt", CMD_TT, 0 }, |
269 | { "underline", CMD_UNDERLINE, 0 }, |
270 | { "unicode", CMD_UNICODE, 0 }, |
271 | { "value", CMD_VALUE, 0 }, |
272 | { "warning", CMD_WARNING, 0 }, |
273 | { "qml", CMD_QML, 0 }, |
274 | { "endqml", CMD_ENDQML, 0 }, |
275 | { "cpp", CMD_CPP, 0 }, |
276 | { "endcpp", CMD_ENDCPP, 0 }, |
277 | { "qmltext", CMD_QMLTEXT, 0 }, |
278 | { "endqmltext", CMD_ENDQMLTEXT, 0 }, |
279 | { "cpptext", CMD_CPPTEXT, 0 }, |
280 | { "endcpptext", CMD_ENDCPPTEXT, 0 }, |
281 | { "js", CMD_JS, 0 }, |
282 | { "endjs", CMD_ENDJS, 0 }, |
283 | { 0, 0, 0 } |
284 | }; |
285 | |
286 | typedef QHash<QString, int> QHash_QString_int; |
287 | typedef QHash<QString, Macro> QHash_QString_Macro; |
288 | |
289 | Q_GLOBAL_STATIC(QStringMap, aliasMap) |
290 | Q_GLOBAL_STATIC(QHash_QString_int, cmdHash) |
291 | Q_GLOBAL_STATIC(QHash_QString_Macro, macroHash) |
292 | |
293 | class DocPrivateExtra |
294 | { |
295 | public: |
296 | QString baseName; |
297 | Doc::Sections granularity; |
298 | Doc::Sections section; // ### |
299 | QList<Atom*> tableOfContents; |
300 | QList<int> tableOfContentsLevels; |
301 | QList<Atom*> keywords; |
302 | QList<Atom*> targets; |
303 | QStringMultiMap metaMap; |
304 | |
305 | DocPrivateExtra() |
306 | : granularity(Doc::Part) { } |
307 | }; |
308 | |
309 | struct Shared // ### get rid of |
310 | { |
311 | Shared() |
312 | : count(1) { } |
313 | void ref() { ++count; } |
314 | bool deref() { return (--count == 0); } |
315 | |
316 | int count; |
317 | }; |
318 | |
319 | static QString cleanLink(const QString &link) |
320 | { |
321 | int colonPos = link.indexOf( |
322 | if ((colonPos == -1) || |
323 | (!link.startsWith("file:") && !link.startsWith( "mailto:"))) |
324 | return link; |
325 | return link.mid(colonPos + 1).simplified(); |
326 | } |
327 | |
328 | class DocPrivate : public Shared |
329 | { |
330 | public: |
331 | DocPrivate(const Location& start = Location::null, |
332 | const Location& end = Location::null, |
333 | const QString& source = ""); |
334 | ~DocPrivate(); |
335 | |
336 | void addAlso(const Text& also); |
337 | void constructExtra(); |
338 | bool isEnumDocSimplifiable() const; |
339 | |
340 | // ### move some of this in DocPrivateExtra |
341 | Location start_loc; |
342 | Location end_loc; |
343 | QString src; |
344 | Text text; |
345 | QSet<QString> params; |
346 | QList<Text> alsoList; |
347 | QStringList enumItemList; |
348 | QStringList omitEnumItemList; |
349 | QSet<QString> metacommandsUsed; |
350 | QCommandMap metaCommandMap; |
351 | bool hasLegalese : 1; |
352 | bool hasSectioningUnits : 1; |
353 | DocPrivateExtra *extra; |
354 | }; |
355 | |
356 | DocPrivate::DocPrivate(const Location& start, |
357 | const Location& end, |
358 | const QString& source) |
359 | : start_loc(start), |
360 | end_loc(end), |
361 | src(source), |
362 | hasLegalese(false), |
363 | hasSectioningUnits(false), |
364 | extra(0) |
365 | { |
366 | // nothing. |
367 | } |
368 | |
369 | DocPrivate::~DocPrivate() |
370 | { |
371 | delete extra; |
372 | } |
373 | |
374 | void DocPrivate::addAlso(const Text& also) |
375 | { |
376 | alsoList.append(also); |
377 | } |
378 | |
379 | void DocPrivate::constructExtra() |
380 | { |
381 | if (extra == 0) |
382 | extra = new DocPrivateExtra; |
383 | } |
384 | |
385 | bool DocPrivate::isEnumDocSimplifiable() const |
386 | { |
387 | bool justMetColon = false; |
388 | int numValueTables = 0; |
389 | |
390 | const Atom *atom = text.firstAtom(); |
391 | while (atom) { |
392 | if (atom->type() == Atom::AutoLink || atom->type() == Atom::String) { |
393 | justMetColon = atom->string().endsWith(":"); |
394 | } |
395 | else if ((atom->type() == Atom::ListLeft) && |
396 | (atom->string() == ATOM_LIST_VALUE)) { |
397 | if (justMetColon || numValueTables > 0) |
398 | return false; |
399 | ++numValueTables; |
400 | } |
401 | atom = atom->next(); |
402 | } |
403 | return true; |
404 | } |
405 | |
406 | class DocParser |
407 | { |
408 | public: |
409 | void parse(const QString &source, |
410 | DocPrivate *docPrivate, |
411 | const QSet<QString> &metaCommandSet); |
412 | |
413 | static int endCmdFor(int cmd); |
414 | static QString cmdName(int cmd); |
415 | static QString endCmdName(int cmd); |
416 | static QString untabifyEtc(const QString& str); |
417 | static int indentLevel(const QString& str); |
418 | static QString unindent(int level, const QString& str); |
419 | static QString slashed(const QString& str); |
420 | |
421 | static int tabSize; |
422 | static QStringList exampleFiles; |
423 | static QStringList exampleDirs; |
424 | static QStringList sourceFiles; |
425 | static QStringList sourceDirs; |
426 | static bool quoting; |
427 | |
428 | private: |
429 | Location& location(); |
430 | QString detailsUnknownCommand(const QSet<QString>& metaCommandSet, |
431 | const QString& str); |
432 | void checkExpiry(const QString& date); |
433 | void insertBaseName(const QString &baseName); |
434 | void insertTarget(const QString& target, bool keyword); |
435 | void include(const QString& fileName, const QString& identifier); |
436 | void startFormat(const QString& format, int cmd); |
437 | bool openCommand(int cmd); |
438 | bool closeCommand(int endCmd); |
439 | void startSection(Doc::Sections unit, int cmd); |
440 | void endSection(int unit, int endCmd); |
441 | void parseAlso(); |
442 | void append(Atom::Type type, const QString& string = ""); |
443 | void append(Atom::Type type, const QString& p1, const QString& p2); |
444 | void appendChar(QChar ch); |
445 | void appendWord(const QString &word); |
446 | void appendToCode(const QString &code); |
447 | void appendToCode(const QString &code, Atom::Type defaultType); |
448 | void startNewPara(); |
449 | void enterPara(Atom::Type leftType = Atom::ParaLeft, |
450 | Atom::Type rightType = Atom::ParaRight, |
451 | const QString& string = ""); |
452 | void leavePara(); |
453 | void leaveValue(); |
454 | void leaveValueList(); |
455 | void leaveTableRow(); |
456 | CodeMarker *quoteFromFile(); |
457 | void expandMacro(const QString& name, const QString& def, int numParams); |
458 | QString expandMacroToString(const QString &name, const QString &def, int numParams); |
459 | Doc::Sections getSectioningUnit(); |
460 | QString getArgument(bool verbatim = false); |
461 | QString getOptionalArgument(); |
462 | QString getRestOfLine(); |
463 | QString getMetaCommandArgument(const QString &cmdStr); |
464 | QString getUntilEnd(int cmd); |
465 | QString getCode(int cmd, CodeMarker *marker); |
466 | QString getUnmarkedCode(int cmd); |
467 | |
468 | bool isBlankLine(); |
469 | bool isLeftBraceAhead(); |
470 | void skipSpacesOnLine(); |
471 | void skipSpacesOrOneEndl(); |
472 | void skipAllSpaces(); |
473 | void skipToNextPreprocessorCommand(); |
474 | |
475 | QStack<int> openedInputs; |
476 | |
477 | QString in; |
478 | int pos; |
479 | int len; |
480 | Location cachedLoc; |
481 | int cachedPos; |
482 | |
483 | DocPrivate* priv; |
484 | enum ParagraphState { |
485 | OutsideParagraph, |
486 | InSingleLineParagraph, |
487 | InMultiLineParagraph |
488 | }; |
489 | ParagraphState paraState; |
490 | bool inTableHeader; |
491 | bool inTableRow; |
492 | bool inTableItem; |
493 | bool indexStartedPara; // ### rename |
494 | Atom::Type pendingParaLeftType; |
495 | Atom::Type pendingParaRightType; |
496 | QString pendingParaString; |
497 | |
498 | int braceDepth; |
499 | int minIndent; |
500 | Doc::Sections currentSection; |
501 | QMap<QString, Location> targetMap; |
502 | QMap<int, QString> pendingFormats; |
503 | QStack<int> openedCommands; |
504 | QStack<OpenedList> openedLists; |
505 | Quoter quoter; |
506 | }; |
507 | |
508 | int DocParser::tabSize; |
509 | QStringList DocParser::exampleFiles; |
510 | QStringList DocParser::exampleDirs; |
511 | QStringList DocParser::sourceFiles; |
512 | QStringList DocParser::sourceDirs; |
513 | bool DocParser::quoting; |
514 | |
515 | /*! |
516 | Parse the \a source string to build a Text data structure |
517 | in \a docPrivate. The Text data structure is a linked list |
518 | of Atoms. |
519 | |
520 | \a metaCommandSet is the set of metacommands that may be |
521 | found in \a source. These metacommands are not markup text |
522 | commands. They are topic commands and related metacommands. |
523 | */ |
524 | void DocParser::parse(const QString& source, |
525 | DocPrivate *docPrivate, |
526 | const QSet<QString>& metaCommandSet) |
527 | { |
528 | in = source; |
529 | pos = 0; |
530 | len = in.length(); |
531 | cachedLoc = docPrivate->start_loc; |
532 | cachedPos = 0; |
533 | priv = docPrivate; |
534 | priv->text << Atom::Nop; |
535 | |
536 | paraState = OutsideParagraph; |
537 | inTableHeader = false; |
538 | inTableRow = false; |
539 | inTableItem = false; |
540 | indexStartedPara = false; |
541 | pendingParaLeftType = Atom::Nop; |
542 | pendingParaRightType = Atom::Nop; |
543 | |
544 | braceDepth = 0; |
545 | minIndent = INT_MAX; |
546 | currentSection = Doc::NoSection; |
547 | openedCommands.push(CMD_OMIT); |
548 | quoter.reset(); |
549 | |
550 | CodeMarker *marker = 0; |
551 | Atom *currentLinkAtom = 0; |
552 | QString p1, p2; |
553 | QStack<bool> preprocessorSkipping; |
554 | int numPreprocessorSkipping = 0; |
555 | |
556 | while (pos < len) { |
557 | QChar ch = in.at(pos); |
558 | |
559 | switch (ch.unicode()) { |
560 | case |
561 | { |
562 | QString cmdStr; |
563 | pos++; |
564 | while (pos < len) { |
565 | ch = in.at(pos); |
566 | if (ch.isLetterOrNumber()) { |
567 | cmdStr += ch; |
568 | pos++; |
569 | } |
570 | else { |
571 | break; |
572 | } |
573 | } |
574 | if (cmdStr.isEmpty()) { |
575 | if (pos < len) { |
576 | enterPara(); |
577 | if (in.at(pos).isSpace()) { |
578 | skipAllSpaces(); |
579 | appendChar(QLatin1Char( |
580 | } |
581 | else { |
582 | appendChar(in.at(pos++)); |
583 | } |
584 | } |
585 | } |
586 | else { |
587 | int cmd = cmdHash()->value(cmdStr,NOT_A_CMD); |
588 | switch (cmd) { |
589 | case CMD_A: |
590 | enterPara(); |
591 | p1 = getArgument(); |
592 | append(Atom::FormattingLeft,ATOM_FORMATTING_PARAMETER); |
593 | append(Atom::String, p1); |
594 | append(Atom::FormattingRight,ATOM_FORMATTING_PARAMETER); |
595 | priv->params.insert(p1); |
596 | break; |
597 | case CMD_ABSTRACT: |
598 | if (openCommand(cmd)) { |
599 | leavePara(); |
600 | append(Atom::AbstractLeft); |
601 | } |
602 | break; |
603 | case CMD_BADCODE: |
604 | leavePara(); |
605 | append(Atom::CodeBad,getCode(CMD_BADCODE, marker)); |
606 | break; |
607 | case CMD_BASENAME: |
608 | leavePara(); |
609 | insertBaseName(getArgument()); |
610 | break; |
611 | case CMD_BOLD: |
612 | startFormat(ATOM_FORMATTING_BOLD, cmd); |
613 | break; |
614 | case CMD_BRIEF: |
615 | leavePara(); |
616 | enterPara(Atom::BriefLeft, Atom::BriefRight); |
617 | break; |
618 | case CMD_C: |
619 | enterPara(); |
620 | p1 = untabifyEtc(getArgument(true)); |
621 | marker = CodeMarker::markerForCode(p1); |
622 | append(Atom::C, marker->markedUpCode(p1, 0, location())); |
623 | break; |
624 | case CMD_CAPTION: |
625 | leavePara(); |
626 | enterPara(Atom::CaptionLeft, Atom::CaptionRight); |
627 | break; |
628 | case CMD_CHAPTER: |
629 | startSection(Doc::Chapter, cmd); |
630 | break; |
631 | case CMD_CODE: |
632 | leavePara(); |
633 | append(Atom::Code, getCode(CMD_CODE, 0)); |
634 | break; |
635 | case CMD_QML: |
636 | leavePara(); |
637 | append(Atom::Qml, getCode(CMD_QML, CodeMarker::markerForLanguage(QLatin1String("QML")))); |
638 | break; |
639 | case CMD_QMLTEXT: |
640 | append(Atom::QmlText); |
641 | break; |
642 | case CMD_JS: |
643 | leavePara(); |
644 | append(Atom::JavaScript, getCode(CMD_JS, CodeMarker::markerForLanguage(QLatin1String("JavaScript")))); |
645 | break; |
646 | case CMD_DIV: |
647 | leavePara(); |
648 | p1 = getArgument(true); |
649 | append(Atom::DivLeft, p1); |
650 | openedCommands.push(cmd); |
651 | break; |
652 | case CMD_ENDDIV: |
653 | leavePara(); |
654 | append(Atom::DivRight); |
655 | closeCommand(cmd); |
656 | break; |
657 | case CMD_CODELINE: |
658 | { |
659 | if (!quoting) { |
660 | if (priv->text.lastAtom()->type() == Atom::Code |
661 | && priv->text.lastAtom()->string().endsWith("\n\n")) |
662 | priv->text.lastAtom()->chopString(); |
663 | appendToCode("\n"); |
664 | } |
665 | else { |
666 | append(Atom::CodeQuoteCommand, cmdStr); |
667 | append(Atom::CodeQuoteArgument, " "); |
668 | } |
669 | } |
670 | break; |
671 | case CMD_DOTS: |
672 | { |
673 | if (!quoting) { |
674 | if (priv->text.lastAtom()->type() == Atom::Code |
675 | && priv->text.lastAtom()->string().endsWith("\n\n")) |
676 | priv->text.lastAtom()->chopString(); |
677 | |
678 | QString arg = getOptionalArgument(); |
679 | int indent = 4; |
680 | if (!arg.isEmpty()) |
681 | indent = arg.toInt(); |
682 | for (int i = 0; i < indent; ++i) |
683 | appendToCode(" "); |
684 | appendToCode("...\n"); |
685 | } |
686 | else { |
687 | append(Atom::CodeQuoteCommand, cmdStr); |
688 | QString arg = getOptionalArgument(); |
689 | if (arg.isEmpty()) |
690 | arg = "4"; |
691 | append(Atom::CodeQuoteArgument, arg); |
692 | } |
693 | } |
694 | break; |
695 | case CMD_ELSE: |
696 | if (preprocessorSkipping.size() > 0) { |
697 | if (preprocessorSkipping.top()) { |
698 | --numPreprocessorSkipping; |
699 | } |
700 | else { |
701 | ++numPreprocessorSkipping; |
702 | } |
703 | preprocessorSkipping.top() = !preprocessorSkipping.top(); |
704 | (void)getRestOfLine(); // ### should ensure that it's empty |
705 | if (numPreprocessorSkipping) |
706 | skipToNextPreprocessorCommand(); |
707 | } |
708 | else { |
709 | location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ELSE))); |
710 | } |
711 | break; |
712 | case CMD_ENDABSTRACT: |
713 | if (closeCommand(cmd)) { |
714 | leavePara(); |
715 | append(Atom::AbstractRight); |
716 | } |
717 | break; |
718 | case CMD_ENDCHAPTER: |
719 | endSection(Doc::Chapter, cmd); |
720 | break; |
721 | case CMD_ENDCODE: |
722 | closeCommand(cmd); |
723 | break; |
724 | case CMD_ENDQML: |
725 | closeCommand(cmd); |
726 | break; |
727 | case CMD_ENDQMLTEXT: |
728 | append(Atom::EndQmlText); |
729 | break; |
730 | case CMD_ENDJS: |
731 | closeCommand(cmd); |
732 | break; |
733 | case CMD_ENDFOOTNOTE: |
734 | if (closeCommand(cmd)) { |
735 | leavePara(); |
736 | append(Atom::FootnoteRight); |
737 | paraState = InMultiLineParagraph; // ### |
738 | } |
739 | break; |
740 | case CMD_ENDIF: |
741 | if (preprocessorSkipping.count() > 0) { |
742 | if (preprocessorSkipping.pop()) |
743 | --numPreprocessorSkipping; |
744 | (void)getRestOfLine(); // ### should ensure that it's empty |
745 | if (numPreprocessorSkipping) |
746 | skipToNextPreprocessorCommand(); |
747 | } |
748 | else { |
749 | location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ENDIF))); |
750 | } |
751 | break; |
752 | case CMD_ENDLEGALESE: |
753 | if (closeCommand(cmd)) { |
754 | leavePara(); |
755 | append(Atom::LegaleseRight); |
756 | } |
757 | break; |
758 | case CMD_ENDLINK: |
759 | if (closeCommand(cmd)) { |
760 | if (priv->text.lastAtom()->type() == Atom::String |
761 | && priv->text.lastAtom()->string().endsWith(" ")) |
762 | priv->text.lastAtom()->chopString(); |
763 | append(Atom::FormattingRight, ATOM_FORMATTING_LINK); |
764 | } |
765 | break; |
766 | case CMD_ENDLIST: |
767 | if (closeCommand(cmd)) { |
768 | leavePara(); |
769 | if (openedLists.top().isStarted()) { |
770 | append(Atom::ListItemRight, |
771 | openedLists.top().styleString()); |
772 | append(Atom::ListRight, |
773 | openedLists.top().styleString()); |
774 | } |
775 | openedLists.pop(); |
776 | } |
777 | break; |
778 | case CMD_ENDOMIT: |
779 | closeCommand(cmd); |
780 | break; |
781 | case CMD_ENDPART: |
782 | endSection(Doc::Part, cmd); |
783 | break; |
784 | case CMD_ENDQUOTATION: |
785 | if (closeCommand(cmd)) { |
786 | leavePara(); |
787 | append(Atom::QuotationRight); |
788 | } |
789 | break; |
790 | case CMD_ENDRAW: |
791 | location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ENDRAW))); |
792 | break; |
793 | case CMD_ENDSECTION1: |
794 | endSection(Doc::Section1, cmd); |
795 | break; |
796 | case CMD_ENDSECTION2: |
797 | endSection(Doc::Section2, cmd); |
798 | break; |
799 | case CMD_ENDSECTION3: |
800 | endSection(Doc::Section3, cmd); |
801 | break; |
802 | case CMD_ENDSECTION4: |
803 | endSection(Doc::Section4, cmd); |
804 | break; |
805 | case CMD_ENDSIDEBAR: |
806 | if (closeCommand(cmd)) { |
807 | leavePara(); |
808 | append(Atom::SidebarRight); |
809 | } |
810 | break; |
811 | case CMD_ENDTABLE: |
812 | if (closeCommand(cmd)) { |
813 | leaveTableRow(); |
814 | append(Atom::TableRight); |
815 | } |
816 | break; |
817 | case CMD_EXPIRE: |
818 | checkExpiry(getArgument()); |
819 | break; |
820 | case CMD_FOOTNOTE: |
821 | if (openCommand(cmd)) { |
822 | enterPara(); |
823 | append(Atom::FootnoteLeft); |
824 | paraState = OutsideParagraph; // ### |
825 | } |
826 | break; |
827 | case CMD_ANNOTATEDLIST: |
828 | append(Atom::AnnotatedList, getArgument()); |
829 | break; |
830 | case CMD_SINCELIST: |
831 | append(Atom::SinceList, getRestOfLine().simplified()); |
832 | break; |
833 | case CMD_GENERATELIST: |
834 | append(Atom::GeneratedList, getArgument()); |
835 | break; |
836 | case CMD_GRANULARITY: |
837 | priv->constructExtra(); |
838 | priv->extra->granularity = getSectioningUnit(); |
839 | break; |
840 | case CMD_HEADER: |
841 | if (openedCommands.top() == CMD_TABLE) { |
842 | leaveTableRow(); |
843 | append(Atom::TableHeaderLeft); |
844 | inTableHeader = true; |
845 | } |
846 | else { |
847 | if (openedCommands.contains(CMD_TABLE)) { |
848 | location().warning(tr("Cannot use '\\%1' within '\\%2'") |
849 | .arg(cmdName(CMD_HEADER)) |
850 | .arg(cmdName(openedCommands.top()))); |
851 | } |
852 | else { |
853 | location().warning(tr("Cannot use '\\%1' outside of '\\%2'") |
854 | .arg(cmdName(CMD_HEADER)) |
855 | .arg(cmdName(CMD_TABLE))); |
856 | } |
857 | } |
858 | break; |
859 | case CMD_I: |
860 | startFormat(ATOM_FORMATTING_ITALIC, cmd); |
861 | break; |
862 | case CMD_IF: |
863 | preprocessorSkipping.push(!Tokenizer::isTrue(getRestOfLine())); |
864 | if (preprocessorSkipping.top()) |
865 | ++numPreprocessorSkipping; |
866 | if (numPreprocessorSkipping) |
867 | skipToNextPreprocessorCommand(); |
868 | break; |
869 | case CMD_IMAGE: |
870 | leaveValueList(); |
871 | append(Atom::Image, getArgument()); |
872 | append(Atom::ImageText, getRestOfLine()); |
873 | break; |
874 | case CMD_INCLUDE: |
875 | { |
876 | QString fileName = getArgument(); |
877 | QString identifier = getRestOfLine(); |
878 | include(fileName, identifier); |
879 | } |
880 | break; |
881 | case CMD_INLINEIMAGE: |
882 | enterPara(); |
883 | append(Atom::InlineImage, getArgument()); |
884 | append(Atom::ImageText, getRestOfLine()); |
885 | append(Atom::String, " "); |
886 | break; |
887 | case CMD_INDEX: |
888 | if (paraState == OutsideParagraph) { |
889 | enterPara(); |
890 | indexStartedPara = true; |
891 | } |
892 | else { |
893 | const Atom *last = priv->text.lastAtom(); |
894 | if (indexStartedPara && |
895 | (last->type() != Atom::FormattingRight || |
896 | last->string() != ATOM_FORMATTING_INDEX)) |
897 | indexStartedPara = false; |
898 | } |
899 | startFormat(ATOM_FORMATTING_INDEX, cmd); |
900 | break; |
901 | case CMD_KEYWORD: |
902 | insertTarget(getRestOfLine(),true); |
903 | break; |
904 | case CMD_L: |
905 | enterPara(); |
906 | if (isLeftBraceAhead()) { |
907 | p1 = getArgument(); |
908 | append(Atom::Link, p1); |
909 | if (isLeftBraceAhead()) { |
910 | currentLinkAtom = priv->text.lastAtom(); |
911 | startFormat(ATOM_FORMATTING_LINK, cmd); |
912 | } |
913 | else { |
914 | append(Atom::FormattingLeft, ATOM_FORMATTING_LINK); |
915 | append(Atom::String, cleanLink(p1)); |
916 | append(Atom::FormattingRight, ATOM_FORMATTING_LINK); |
917 | } |
918 | } |
919 | else { |
920 | p1 = getArgument(); |
921 | append(Atom::Link, p1); |
922 | append(Atom::FormattingLeft, ATOM_FORMATTING_LINK); |
923 | append(Atom::String, cleanLink(p1)); |
924 | append(Atom::FormattingRight, ATOM_FORMATTING_LINK); |
925 | } |
926 | break; |
927 | case CMD_LEGALESE: |
928 | leavePara(); |
929 | if (openCommand(cmd)) |
930 | append(Atom::LegaleseLeft); |
931 | docPrivate->hasLegalese = true; |
932 | break; |
933 | case CMD_LINK: |
934 | if (openCommand(cmd)) { |
935 | enterPara(); |
936 | p1 = getArgument(); |
937 | append(Atom::Link, p1); |
938 | append(Atom::FormattingLeft, ATOM_FORMATTING_LINK); |
939 | skipSpacesOrOneEndl(); |
940 | } |
941 | break; |
942 | case CMD_LIST: |
943 | if (openCommand(cmd)) { |
944 | leavePara(); |
945 | openedLists.push(OpenedList(location(), |
946 | getOptionalArgument())); |
947 | } |
948 | break; |
949 | case CMD_META: |
950 | priv->constructExtra(); |
951 | p1 = getArgument(); |
952 | priv->extra->metaMap.insert(p1, getArgument()); |
953 | break; |
954 | case CMD_NEWCODE: |
955 | location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_NEWCODE))); |
956 | break; |
957 | case CMD_O: |
958 | leavePara(); |
959 | if (openedCommands.top() == CMD_LIST) { |
960 | if (openedLists.top().isStarted()) { |
961 | append(Atom::ListItemRight, |
962 | openedLists.top().styleString()); |
963 | } |
964 | else { |
965 | append(Atom::ListLeft, |
966 | openedLists.top().styleString()); |
967 | } |
968 | openedLists.top().next(); |
969 | append(Atom::ListItemNumber, |
970 | openedLists.top().numberString()); |
971 | append(Atom::ListItemLeft, |
972 | openedLists.top().styleString()); |
973 | enterPara(); |
974 | } |
975 | else if (openedCommands.top() == CMD_TABLE) { |
976 | p1 = "1,1"; |
977 | if (isLeftBraceAhead()) { |
978 | p1 = getArgument(); |
979 | if (isLeftBraceAhead()) { |
980 | p2 = getArgument(); |
981 | } |
982 | } |
983 | |
984 | if (!inTableHeader && !inTableRow) { |
985 | location().warning(tr("Missing '\\%1' or '\\%1' before '\\%3'") |
986 | .arg(cmdName(CMD_HEADER)) |
987 | .arg(cmdName(CMD_ROW)) |
988 | .arg(cmdName(CMD_O))); |
989 | append(Atom::TableRowLeft); |
990 | inTableRow = true; |
991 | } |
992 | else if (inTableItem) { |
993 | append(Atom::TableItemRight); |
994 | inTableItem = false; |
995 | } |
996 | |
997 | append(Atom::TableItemLeft, p1, p2); |
998 | inTableItem = true; |
999 | } |
1000 | else { |
1001 | location().warning(tr("Command '\\%1' outside of '\\%2' and '\\%3'") |
1002 | .arg(cmdName(cmd)) |
1003 | .arg(cmdName(CMD_LIST)) |
1004 | .arg(cmdName(CMD_TABLE))); |
1005 | } |
1006 | break; |
1007 | case CMD_OLDCODE: |
1008 | leavePara(); |
1009 | append(Atom::CodeOld, getCode(CMD_OLDCODE, marker)); |
1010 | append(Atom::CodeNew, getCode(CMD_NEWCODE, marker)); |
1011 | break; |
1012 | case CMD_OMIT: |
1013 | getUntilEnd(cmd); |
1014 | break; |
1015 | case CMD_OMITVALUE: |
1016 | p1 = getArgument(); |
1017 | if (!priv->enumItemList.contains(p1)) |
1018 | priv->enumItemList.append(p1); |
1019 | if (!priv->omitEnumItemList.contains(p1)) |
1020 | priv->omitEnumItemList.append(p1); |
1021 | break; |
1022 | case CMD_PART: |
1023 | startSection(Doc::Part, cmd); |
1024 | break; |
1025 | case CMD_PRINTLINE: |
1026 | leavePara(); |
1027 | if (!quoting) |
1028 | appendToCode(quoter.quoteLine(location(), cmdStr, |
1029 | getRestOfLine())); |
1030 | else { |
1031 | append(Atom::CodeQuoteCommand, cmdStr); |
1032 | append(Atom::CodeQuoteArgument, getRestOfLine()); |
1033 | } |
1034 | break; |
1035 | case CMD_PRINTTO: |
1036 | leavePara(); |
1037 | if (!quoting) |
1038 | appendToCode(quoter.quoteTo(location(), cmdStr, |
1039 | getRestOfLine())); |
1040 | else { |
1041 | append(Atom::CodeQuoteCommand, cmdStr); |
1042 | append(Atom::CodeQuoteArgument, getRestOfLine()); |
1043 | } |
1044 | break; |
1045 | case CMD_PRINTUNTIL: |
1046 | leavePara(); |
1047 | if (!quoting) |
1048 | appendToCode(quoter.quoteUntil(location(), cmdStr, |
1049 | getRestOfLine())); |
1050 | else { |
1051 | append(Atom::CodeQuoteCommand, cmdStr); |
1052 | append(Atom::CodeQuoteArgument, getRestOfLine()); |
1053 | } |
1054 | break; |
1055 | case CMD_QUOTATION: |
1056 | if (openCommand(cmd)) { |
1057 | leavePara(); |
1058 | append(Atom::QuotationLeft); |
1059 | } |
1060 | break; |
1061 | case CMD_QUOTEFILE: |
1062 | { |
1063 | leavePara(); |
1064 | QString fileName = getArgument(); |
1065 | Doc::quoteFromFile(location(), quoter, fileName); |
1066 | if (!quoting) { |
1067 | append(Atom::Code, |
1068 | quoter.quoteTo(location(), cmdStr, "")); |
1069 | quoter.reset(); |
1070 | } |
1071 | else { |
1072 | append(Atom::CodeQuoteCommand, cmdStr); |
1073 | append(Atom::CodeQuoteArgument, fileName); |
1074 | } |
1075 | break; |
1076 | } |
1077 | case CMD_QUOTEFROMFILE: |
1078 | leavePara(); |
1079 | if (!quoting) |
1080 | quoteFromFile(); |
1081 | else { |
1082 | append(Atom::CodeQuoteCommand, cmdStr); |
1083 | append(Atom::CodeQuoteArgument, getArgument()); |
1084 | } |
1085 | break; |
1086 | case CMD_QUOTEFUNCTION: |
1087 | leavePara(); |
1088 | marker = quoteFromFile(); |
1089 | p1 = getRestOfLine(); |
1090 | if (!quoting) { |
1091 | quoter.quoteTo(location(), cmdStr, |
1092 | slashed(marker->functionBeginRegExp(p1))); |
1093 | append(Atom::Code, |
1094 | quoter.quoteUntil(location(), cmdStr, |
1095 | slashed(marker->functionEndRegExp(p1)))); |
1096 | quoter.reset(); |
1097 | } |
1098 | else { |
1099 | append(Atom::CodeQuoteCommand, cmdStr); |
1100 | append(Atom::CodeQuoteArgument, slashed(marker->functionEndRegExp(p1))); |
1101 | } |
1102 | break; |
1103 | case CMD_RAW: |
1104 | leavePara(); |
1105 | p1 = getRestOfLine(); |
1106 | if (p1.isEmpty()) |
1107 | location().warning(tr("Missing format name after '\\%1") |
1108 | .arg(cmdName(CMD_RAW))); |
1109 | append(Atom::FormatIf, p1); |
1110 | append(Atom::RawString, untabifyEtc(getUntilEnd(cmd))); |
1111 | append(Atom::FormatElse); |
1112 | append(Atom::FormatEndif); |
1113 | break; |
1114 | case CMD_ROW: |
1115 | if (openedCommands.top() == CMD_TABLE) { |
1116 | p1.clear(); |
1117 | if (isLeftBraceAhead()) |
1118 | p1 = getArgument(true); |
1119 | leaveTableRow(); |
1120 | append(Atom::TableRowLeft,p1); |
1121 | inTableRow = true; |
1122 | } |
1123 | else { |
1124 | if (openedCommands.contains(CMD_TABLE)) { |
1125 | location().warning(tr("Cannot use '\\%1' within '\\%2'") |
1126 | .arg(cmdName(CMD_ROW)) |
1127 | .arg(cmdName(openedCommands.top()))); |
1128 | } |
1129 | else { |
1130 | location().warning(tr("Cannot use '\\%1' outside of '\\%2'") |
1131 | .arg(cmdName(CMD_ROW)) |
1132 | .arg(cmdName(CMD_TABLE))); |
1133 | } |
1134 | } |
1135 | break; |
1136 | case CMD_SA: |
1137 | parseAlso(); |
1138 | break; |
1139 | case CMD_SECTION1: |
1140 | startSection(Doc::Section1, cmd); |
1141 | break; |
1142 | case CMD_SECTION2: |
1143 | startSection(Doc::Section2, cmd); |
1144 | break; |
1145 | case CMD_SECTION3: |
1146 | startSection(Doc::Section3, cmd); |
1147 | break; |
1148 | case CMD_SECTION4: |
1149 | startSection(Doc::Section4, cmd); |
1150 | break; |
1151 | case CMD_SIDEBAR: |
1152 | if (openCommand(cmd)) { |
1153 | leavePara(); |
1154 | append(Atom::SidebarLeft); |
1155 | } |
1156 | break; |
1157 | case CMD_SKIPLINE: |
1158 | leavePara(); |
1159 | if (!quoting) |
1160 | quoter.quoteLine(location(), |
1161 | cmdStr, |
1162 | getRestOfLine()); |
1163 | else { |
1164 | append(Atom::CodeQuoteCommand, cmdStr); |
1165 | append(Atom::CodeQuoteArgument, getRestOfLine()); |
1166 | } |
1167 | break; |
1168 | case CMD_SKIPTO: |
1169 | leavePara(); |
1170 | if (!quoting) |
1171 | quoter.quoteTo(location(), |
1172 | cmdStr, |
1173 | getRestOfLine()); |
1174 | else { |
1175 | append(Atom::CodeQuoteCommand, cmdStr); |
1176 | append(Atom::CodeQuoteArgument, getRestOfLine()); |
1177 | } |
1178 | break; |
1179 | case CMD_SKIPUNTIL: |
1180 | leavePara(); |
1181 | if (!quoting) |
1182 | quoter.quoteUntil(location(), |
1183 | cmdStr, |
1184 | getRestOfLine()); |
1185 | else { |
1186 | append(Atom::CodeQuoteCommand, cmdStr); |
1187 | append(Atom::CodeQuoteArgument, getRestOfLine()); |
1188 | } |
1189 | break; |
1190 | case CMD_SPAN: |
1191 | p1 = ATOM_FORMATTING_SPAN + getArgument(true); |
1192 | startFormat(p1, cmd); |
1193 | break; |
1194 | case CMD_SNIPPET: |
1195 | leavePara(); |
1196 | { |
1197 | QString snippet = getArgument(); |
1198 | QString identifier = getRestOfLine(); |
1199 | if (quoting) { |
1200 | append(Atom::SnippetCommand, cmdStr); |
1201 | append(Atom::SnippetLocation, snippet); |
1202 | append(Atom::SnippetIdentifier, identifier); |
1203 | } |
1204 | else { |
1205 | marker = Doc::quoteFromFile(location(),quoter,snippet); |
1206 | appendToCode(quoter.quoteSnippet(location(), identifier), marker->atomType()); |
1207 | } |
1208 | } |
1209 | break; |
1210 | case CMD_SUB: |
1211 | startFormat(ATOM_FORMATTING_SUBSCRIPT, cmd); |
1212 | break; |
1213 | case CMD_SUP: |
1214 | startFormat(ATOM_FORMATTING_SUPERSCRIPT, cmd); |
1215 | break; |
1216 | case CMD_TABLE: |
1217 | p1 = getRestOfLine(); |
1218 | if (openCommand(cmd)) { |
1219 | leavePara(); |
1220 | append(Atom::TableLeft, p1); |
1221 | inTableHeader = false; |
1222 | inTableRow = false; |
1223 | inTableItem = false; |
1224 | } |
1225 | break; |
1226 | case CMD_TABLEOFCONTENTS: |
1227 | p1 = "1"; |
1228 | if (isLeftBraceAhead()) |
1229 | p1 = getArgument(); |
1230 | p1 += ","; |
1231 | p1 += QString::number((int)getSectioningUnit()); |
1232 | append(Atom::TableOfContents, p1); |
1233 | break; |
1234 | case CMD_TARGET: |
1235 | insertTarget(getRestOfLine(),false); |
1236 | break; |
1237 | case CMD_TT: |
1238 | startFormat(ATOM_FORMATTING_TELETYPE, cmd); |
1239 | break; |
1240 | case CMD_UNDERLINE: |
1241 | startFormat(ATOM_FORMATTING_UNDERLINE, cmd); |
1242 | break; |
1243 | case CMD_UNICODE: |
1244 | enterPara(); |
1245 | p1 = getArgument(); |
1246 | { |
1247 | bool ok; |
1248 | uint unicodeChar = p1.toUInt(&ok, 0); |
1249 | if (!ok || |
1250 | (unicodeChar == 0x0000) || |
1251 | (unicodeChar > 0xFFFE)) { |
1252 | location().warning(tr("Invalid Unicode character '%1' specified " |
1253 | "with '%2'") |
1254 | .arg(p1, cmdName(CMD_UNICODE))); |
1255 | } |
1256 | else { |
1257 | append(Atom::String, QChar(unicodeChar)); |
1258 | } |
1259 | } |
1260 | break; |
1261 | case CMD_VALUE: |
1262 | leaveValue(); |
1263 | if (openedLists.top().style() == OpenedList::Value) { |
1264 | p1 = getArgument(); |
1265 | if (!priv->enumItemList.contains(p1)) |
1266 | priv->enumItemList.append(p1); |
1267 | |
1268 | openedLists.top().next(); |
1269 | append(Atom::ListTagLeft, ATOM_LIST_VALUE); |
1270 | append(Atom::String, p1); |
1271 | append(Atom::ListTagRight, ATOM_LIST_VALUE); |
1272 | append(Atom::ListItemLeft, ATOM_LIST_VALUE); |
1273 | |
1274 | skipSpacesOrOneEndl(); |
1275 | if (isBlankLine()) |
1276 | append(Atom::Nop); |
1277 | } |
1278 | else { |
1279 | // ### problems |
1280 | } |
1281 | break; |
1282 | case CMD_WARNING: |
1283 | leavePara(); |
1284 | enterPara(); |
1285 | append(Atom::FormattingLeft, ATOM_FORMATTING_BOLD); |
1286 | append(Atom::String, "Warning:"); |
1287 | append(Atom::FormattingRight, ATOM_FORMATTING_BOLD); |
1288 | append(Atom::String, " "); |
1289 | break; |
1290 | case CMD_OVERLOAD: |
1291 | priv->metacommandsUsed.insert(cmdStr); |
1292 | p1.clear(); |
1293 | if (!isBlankLine()) |
1294 | p1 = getRestOfLine(); |
1295 | if (!p1.isEmpty()) { |
1296 | append(Atom::ParaLeft); |
1297 | append(Atom::String, "This function overloads "); |
1298 | append(Atom::AutoLink,p1); |
1299 | append(Atom::String, "."); |
1300 | append(Atom::ParaRight); |
1301 | } |
1302 | else { |
1303 | append(Atom::ParaLeft); |
1304 | append(Atom::String,"This is an overloaded function."); |
1305 | append(Atom::ParaRight); |
1306 | p1 = getMetaCommandArgument(cmdStr); |
1307 | } |
1308 | priv->metaCommandMap[cmdStr].append(p1); |
1309 | break; |
1310 | case NOT_A_CMD: |
1311 | if (metaCommandSet.contains(cmdStr)) { |
1312 | priv->metacommandsUsed.insert(cmdStr); |
1313 | QString xxx = getMetaCommandArgument(cmdStr); |
1314 | priv->metaCommandMap[cmdStr].append(xxx); |
1315 | } |
1316 | else if (macroHash()->contains(cmdStr)) { |
1317 | const Macro ¯o = macroHash()->value(cmdStr); |
1318 | int numPendingFi = 0; |
1319 | QStringMap::ConstIterator d; |
1320 | d = macro.otherDefs.begin(); |
1321 | while (d != macro.otherDefs.end()) { |
1322 | append(Atom::FormatIf, d.key()); |
1323 | expandMacro(cmdStr, *d, macro.numParams); |
1324 | ++d; |
1325 | |
1326 | if (d == macro.otherDefs.end()) { |
1327 | append(Atom::FormatEndif); |
1328 | } |
1329 | else { |
1330 | append(Atom::FormatElse); |
1331 | numPendingFi++; |
1332 | } |
1333 | } |
1334 | while (numPendingFi-- > 0) |
1335 | append(Atom::FormatEndif); |
1336 | |
1337 | if (!macro.defaultDef.isEmpty()) { |
1338 | if (!macro.otherDefs.isEmpty()) { |
1339 | macro.defaultDefLocation.warning( |
1340 | tr("Macro cannot have both " |
1341 | "format-specific and qdoc- " |
1342 | "syntax definitions")); |
1343 | } |
1344 | else { |
1345 | location().push(macro.defaultDefLocation.filePath()); |
1346 | in.insert(pos, expandMacroToString(cmdStr, macro.defaultDef, macro.numParams)); |
1347 | len = in.length(); |
1348 | openedInputs.push(pos + macro.defaultDef.length()); |
1349 | } |
1350 | } |
1351 | } |
1352 | else { |
1353 | location().warning( |
1354 | tr("Unknown command '\\%1'").arg(cmdStr), |
1355 | detailsUnknownCommand(metaCommandSet,cmdStr)); |
1356 | enterPara(); |
1357 | append(Atom::UnknownCommand, cmdStr); |
1358 | } |
1359 | } |
1360 | } |
1361 | } |
1362 | break; |
1363 | case |
1364 | enterPara(); |
1365 | appendChar( |
1366 | braceDepth++; |
1367 | pos++; |
1368 | break; |
1369 | case |
1370 | { |
1371 | braceDepth--; |
1372 | pos++; |
1373 | |
1374 | QMap<int, QString>::Iterator f = pendingFormats.find(braceDepth); |
1375 | if (f == pendingFormats.end()) { |
1376 | enterPara(); |
1377 | appendChar( |
1378 | } |
1379 | else { |
1380 | append(Atom::FormattingRight, *f); |
1381 | if (*f == ATOM_FORMATTING_INDEX) { |
1382 | if (indexStartedPara) |
1383 | skipAllSpaces(); |
1384 | } |
1385 | else if (*f == ATOM_FORMATTING_LINK) { |
1386 | // hack for C++ to support links like |
1387 | // \l{QString::}{count()} |
1388 | if (currentLinkAtom && |
1389 | currentLinkAtom->string().endsWith("::")) { |
1390 | QString suffix = Text::subText(currentLinkAtom, |
1391 | priv->text.lastAtom()).toString(); |
1392 | currentLinkAtom->appendString(suffix); |
1393 | } |
1394 | currentLinkAtom = 0; |
1395 | } |
1396 | pendingFormats.erase(f); |
1397 | } |
1398 | } |
1399 | break; |
1400 | default: |
1401 | { |
1402 | bool newWord; |
1403 | switch (priv->text.lastAtom()->type()) { |
1404 | case Atom::ParaLeft: |
1405 | newWord = true; |
1406 | break; |
1407 | default: |
1408 | newWord = false; |
1409 | } |
1410 | |
1411 | if (paraState == OutsideParagraph) { |
1412 | if (ch.isSpace()) { |
1413 | ++pos; |
1414 | newWord = false; |
1415 | } |
1416 | else { |
1417 | enterPara(); |
1418 | newWord = true; |
1419 | } |
1420 | } |
1421 | else { |
1422 | if (ch.isSpace()) { |
1423 | ++pos; |
1424 | if ((ch == |
1425 | (paraState == InSingleLineParagraph || |
1426 | isBlankLine())) { |
1427 | leavePara(); |
1428 | newWord = false; |
1429 | } |
1430 | else { |
1431 | appendChar( |
1432 | newWord = true; |
1433 | } |
1434 | } |
1435 | else { |
1436 | newWord = true; |
1437 | } |
1438 | } |
1439 | |
1440 | if (newWord) { |
1441 | int startPos = pos; |
1442 | int numInternalUppercase = 0; |
1443 | int numLowercase = 0; |
1444 | int numStrangeSymbols = 0; |
1445 | |
1446 | while (pos < len) { |
1447 | unsigned char latin1Ch = in.at(pos).toLatin1(); |
1448 | if (islower(latin1Ch)) { |
1449 | ++numLowercase; |
1450 | ++pos; |
1451 | } |
1452 | else if (isupper(latin1Ch)) { |
1453 | if (pos > startPos) |
1454 | ++numInternalUppercase; |
1455 | ++pos; |
1456 | } |
1457 | else if (isdigit(latin1Ch)) { |
1458 | if (pos > startPos) { |
1459 | ++pos; |
1460 | } |
1461 | else { |
1462 | break; |
1463 | } |
1464 | } |
1465 | else if (latin1Ch == |
1466 | ++numStrangeSymbols; |
1467 | ++pos; |
1468 | } |
1469 | else if (latin1Ch == |
1470 | && in.at(pos + 1) == QLatin1Char( |
1471 | ++numStrangeSymbols; |
1472 | pos += 2; |
1473 | } |
1474 | else if (latin1Ch == |
1475 | if (pos > startPos) { |
1476 | if (pos < len - 1 && |
1477 | in.at(pos + 1) == QLatin1Char( |
1478 | ++numStrangeSymbols; |
1479 | pos += 2; |
1480 | break; |
1481 | } |
1482 | else { |
1483 | // ### handle functions with signatures |
1484 | // and function calls |
1485 | break; |
1486 | } |
1487 | } |
1488 | else { |
1489 | break; |
1490 | } |
1491 | } |
1492 | else { |
1493 | break; |
1494 | } |
1495 | } |
1496 | |
1497 | if (pos == startPos) { |
1498 | if (!ch.isSpace()) { |
1499 | appendChar(ch); |
1500 | ++pos; |
1501 | } |
1502 | } |
1503 | else { |
1504 | QString word = in.mid(startPos, pos - startPos); |
1505 | // is word a C++ symbol or an English word? |
1506 | if ((numInternalUppercase >= 1 && numLowercase >= 2) |
1507 | || numStrangeSymbols >= 1) { |
1508 | append(Atom::AutoLink, word); |
1509 | } |
1510 | else { |
1511 | appendWord(word); |
1512 | } |
1513 | } |
1514 | } |
1515 | } |
1516 | } |
1517 | } |
1518 | leaveValueList(); |
1519 | |
1520 | // for compatibility |
1521 | if (openedCommands.top() == CMD_LEGALESE) { |
1522 | append(Atom::LegaleseRight); |
1523 | openedCommands.pop(); |
1524 | } |
1525 | |
1526 | if (openedCommands.top() != CMD_OMIT) { |
1527 | location().warning(tr("Missing '\\%1'").arg(endCmdName(openedCommands.top()))); |
1528 | } |
1529 | else if (preprocessorSkipping.count() > 0) { |
1530 | location().warning(tr("Missing '\\%1'").arg(cmdName(CMD_ENDIF))); |
1531 | } |
1532 | |
1533 | if (currentSection > Doc::NoSection) { |
1534 | append(Atom::SectionRight, QString::number(currentSection)); |
1535 | currentSection = Doc::NoSection; |
1536 | } |
1537 | |
1538 | if (priv->extra && priv->extra->granularity < priv->extra->section) |
1539 | priv->extra->granularity = priv->extra->section; |
1540 | priv->text.stripFirstAtom(); |
1541 | } |
1542 | |
1543 | Location &DocParser::location() |
1544 | { |
1545 | while (!openedInputs.isEmpty() && openedInputs.top() <= pos) { |
1546 | cachedLoc.pop(); |
1547 | cachedPos = openedInputs.pop(); |
1548 | } |
1549 | while (cachedPos < pos) |
1550 | cachedLoc.advance(in.at(cachedPos++)); |
1551 | return cachedLoc; |
1552 | } |
1553 | |
1554 | QString DocParser::detailsUnknownCommand(const QSet<QString> &metaCommandSet, |
1555 | const QString &str) |
1556 | { |
1557 | QSet<QString> commandSet = metaCommandSet; |
1558 | int i = 0; |
1559 | while (cmds[i].english != 0) { |
1560 | commandSet.insert(*cmds[i].alias); |
1561 | i++; |
1562 | } |
1563 | |
1564 | if (aliasMap()->contains(str)) |
1565 | return tr("The command '\\%1' was renamed '\\%2' by the configuration" |
1566 | " file. Use the new name.") |
1567 | .arg(str).arg((*aliasMap())[str]); |
1568 | |
1569 | QString best = nearestName(str, commandSet); |
1570 | if (best.isEmpty()) |
1571 | return QString(); |
1572 | return tr("Maybe you meant '\\%1'?").arg(best); |
1573 | } |
1574 | |
1575 | void DocParser::checkExpiry(const QString& date) |
1576 | { |
1577 | QRegExp ymd("(\\d{4})(?:-(\\d{2})(?:-(\\d{2})))"); |
1578 | |
1579 | if (ymd.exactMatch(date)) { |
1580 | int y = ymd.cap(1).toInt(); |
1581 | int m = ymd.cap(2).toInt(); |
1582 | int d = ymd.cap(3).toInt(); |
1583 | |
1584 | if (m == 0) |
1585 | m = 1; |
1586 | if (d == 0) |
1587 | d = 1; |
1588 | QDate expiryDate(y, m, d); |
1589 | if (expiryDate.isValid()) { |
1590 | int days = expiryDate.daysTo(QDate::currentDate()); |
1591 | if (days == 0) { |
1592 | location().warning(tr("Documentation expires today")); |
1593 | } |
1594 | else if (days == 1) { |
1595 | location().warning(tr("Documentation expired yesterday")); |
1596 | } |
1597 | else if (days >= 2) { |
1598 | location().warning(tr("Documentation expired %1 days ago") |
1599 | .arg(days)); |
1600 | } |
1601 | } |
1602 | else { |
1603 | location().warning(tr("Date '%1' invalid").arg(date)); |
1604 | } |
1605 | } |
1606 | else { |
1607 | location().warning(tr("Date '%1' not in YYYY-MM-DD format") |
1608 | .arg(date)); |
1609 | } |
1610 | } |
1611 | |
1612 | void DocParser::insertBaseName(const QString &baseName) |
1613 | { |
1614 | priv->constructExtra(); |
1615 | if (currentSection == priv->extra->section) { |
1616 | priv->extra->baseName = baseName; |
1617 | } |
1618 | else { |
1619 | Atom *atom = priv->text.firstAtom(); |
1620 | Atom *sectionLeft = 0; |
1621 | |
1622 | int delta = currentSection - priv->extra->section; |
1623 | |
1624 | while (atom != 0) { |
1625 | if (atom->type() == Atom::SectionLeft && |
1626 | atom->string().toInt() == delta) |
1627 | sectionLeft = atom; |
1628 | atom = atom->next(); |
1629 | } |
1630 | if (sectionLeft != 0) |
1631 | (void) new Atom(sectionLeft, Atom::BaseName, baseName); |
1632 | } |
1633 | } |
1634 | |
1635 | void DocParser::insertTarget(const QString &target, bool keyword) |
1636 | { |
1637 | if (targetMap.contains(target)) { |
1638 | location().warning(tr("Duplicate target name '%1'").arg(target)); |
1639 | targetMap[target].warning(tr("(The previous occurrence is here)")); |
1640 | } |
1641 | else { |
1642 | targetMap.insert(target, location()); |
1643 | append(Atom::Target, target); |
1644 | priv->constructExtra(); |
1645 | if (keyword) |
1646 | priv->extra->keywords.append(priv->text.lastAtom()); |
1647 | else |
1648 | priv->extra->targets.append(priv->text.lastAtom()); |
1649 | } |
1650 | } |
1651 | |
1652 | void DocParser::include(const QString& fileName, const QString& identifier) |
1653 | { |
1654 | if (location().depth() > 16) |
1655 | location().fatal(tr("Too many nested '\\%1's") |
1656 | .arg(cmdName(CMD_INCLUDE))); |
1657 | |
1658 | QString userFriendlyFilePath; |
1659 | // ### use current directory? |
1660 | QString filePath = Config::findFile(location(), |
1661 | sourceFiles, |
1662 | sourceDirs, |
1663 | fileName, |
1664 | userFriendlyFilePath); |
1665 | if (filePath.isEmpty()) { |
1666 | location().warning(tr("Cannot find qdoc include file '%1'").arg(fileName)); |
1667 | } |
1668 | else { |
1669 | QFile inFile(filePath); |
1670 | if (!inFile.open(QFile::ReadOnly)) { |
1671 | location().warning(tr("Cannot open qdoc include file '%1'") |
1672 | .arg(userFriendlyFilePath)); |
1673 | } |
1674 | else { |
1675 | location().push(userFriendlyFilePath); |
1676 | |
1677 | QTextStream inStream(&inFile); |
1678 | QString includedStuff = inStream.readAll(); |
1679 | inFile.close(); |
1680 | |
1681 | if (identifier.isEmpty()) { |
1682 | in.insert(pos, includedStuff); |
1683 | len = in.length(); |
1684 | openedInputs.push(pos + includedStuff.length()); |
1685 | } |
1686 | else { |
1687 | QStringList lineBuffer = includedStuff.split(QLatin1Char( |
1688 | int i = 0; |
1689 | int startLine = -1; |
1690 | while (i < lineBuffer.size()) { |
1691 | if (lineBuffer[i].startsWith("//!")) { |
1692 | if (lineBuffer[i].contains(identifier)) { |
1693 | startLine = i+1; |
1694 | break; |
1695 | } |
1696 | } |
1697 | ++i; |
1698 | } |
1699 | if (startLine < 0) { |
1700 | location().warning(tr("Cannot find '%1' in '%2'") |
1701 | .arg(identifier) |
1702 | .arg(userFriendlyFilePath)); |
1703 | return; |
1704 | |
1705 | } |
1706 | QString result; |
1707 | i = startLine; |
1708 | do { |
1709 | if (lineBuffer[i].startsWith("//!")) { |
1710 | if (i<lineBuffer.size()) { |
1711 | if (lineBuffer[i].contains(identifier)) { |
1712 | break; |
1713 | } |
1714 | } |
1715 | } |
1716 | else |
1717 | result += lineBuffer[i] + "\n"; |
1718 | ++i; |
1719 | } while (i < lineBuffer.size()); |
1720 | if (result.isEmpty()) { |
1721 | location().warning(tr("Empty qdoc snippet '%1' in '%2'") |
1722 | .arg(identifier) |
1723 | .arg(userFriendlyFilePath)); |
1724 | } |
1725 | else { |
1726 | in.insert(pos, result); |
1727 | len = in.length(); |
1728 | openedInputs.push(pos + result.length()); |
1729 | } |
1730 | } |
1731 | } |
1732 | } |
1733 | } |
1734 | |
1735 | void DocParser::startFormat(const QString& format, int cmd) |
1736 | { |
1737 | enterPara(); |
1738 | |
1739 | QMap<int, QString>::ConstIterator f = pendingFormats.begin(); |
1740 | while (f != pendingFormats.end()) { |
1741 | if (*f == format) { |
1742 | location().warning(tr("Cannot nest '\\%1' commands") |
1743 | .arg(cmdName(cmd))); |
1744 | return; |
1745 | } |
1746 | ++f; |
1747 | } |
1748 | |
1749 | append(Atom::FormattingLeft, format); |
1750 | |
1751 | if (isLeftBraceAhead()) { |
1752 | skipSpacesOrOneEndl(); |
1753 | pendingFormats.insert(braceDepth, format); |
1754 | ++braceDepth; |
1755 | ++pos; |
1756 | } |
1757 | else { |
1758 | append(Atom::String, getArgument()); |
1759 | append(Atom::FormattingRight, format); |
1760 | if (format == ATOM_FORMATTING_INDEX && indexStartedPara) { |
1761 | skipAllSpaces(); |
1762 | indexStartedPara = false; |
1763 | } |
1764 | } |
1765 | } |
1766 | |
1767 | bool DocParser::openCommand(int cmd) |
1768 | { |
1769 | int outer = openedCommands.top(); |
1770 | bool ok = true; |
1771 | |
1772 | if (cmd != CMD_LINK) { |
1773 | if (outer == CMD_LIST) { |
1774 | ok = (cmd == CMD_FOOTNOTE || cmd == CMD_LIST); |
1775 | } |
1776 | else if (outer == CMD_ABSTRACT) { |
1777 | ok = (cmd == CMD_LIST || |
1778 | cmd == CMD_QUOTATION || |
1779 | cmd == CMD_TABLE); |
1780 | } |
1781 | else if (outer == CMD_SIDEBAR) { |
1782 | ok = (cmd == CMD_LIST || |
1783 | cmd == CMD_QUOTATION || |
1784 | cmd == CMD_SIDEBAR); |
1785 | } |
1786 | else if (outer == CMD_QUOTATION) { |
1787 | ok = (cmd == CMD_LIST); |
1788 | } |
1789 | else if (outer == CMD_TABLE) { |
1790 | ok = (cmd == CMD_LIST || |
1791 | cmd == CMD_FOOTNOTE || |
1792 | cmd == CMD_QUOTATION); |
1793 | } |
1794 | else if (outer == CMD_FOOTNOTE || outer == CMD_LINK) { |
1795 | ok = false; |
1796 | } |
1797 | } |
1798 | |
1799 | if (ok) { |
1800 | openedCommands.push(cmd); |
1801 | } |
1802 | else { |
1803 | location().warning(tr("Cannot use '\\%1' within '\\%2'") |
1804 | .arg(cmdName(cmd)).arg(cmdName(outer))); |
1805 | } |
1806 | return ok; |
1807 | } |
1808 | |
1809 | bool DocParser::closeCommand(int endCmd) |
1810 | { |
1811 | if (endCmdFor(openedCommands.top()) == endCmd && openedCommands.size() > 1) { |
1812 | openedCommands.pop(); |
1813 | return true; |
1814 | } |
1815 | else { |
1816 | bool contains = false; |
1817 | QStack<int> opened2 = openedCommands; |
1818 | while (opened2.size() > 1) { |
1819 | if (endCmdFor(opened2.top()) == endCmd) { |
1820 | contains = true; |
1821 | break; |
1822 | } |
1823 | opened2.pop(); |
1824 | } |
1825 | |
1826 | if (contains) { |
1827 | while (endCmdFor(openedCommands.top()) != endCmd && openedCommands.size() > 1) { |
1828 | location().warning(tr("Missing '\\%1' before '\\%2'") |
1829 | .arg(endCmdName(openedCommands.top())) |
1830 | .arg(cmdName(endCmd))); |
1831 | openedCommands.pop(); |
1832 | } |
1833 | } |
1834 | else { |
1835 | location().warning(tr("Unexpected '\\%1'").arg(cmdName(endCmd))); |
1836 | } |
1837 | return false; |
1838 | } |
1839 | } |
1840 | |
1841 | void DocParser::startSection(Doc::Sections unit, int cmd) |
1842 | { |
1843 | leaveValueList(); |
1844 | |
1845 | if (currentSection == Doc::NoSection) { |
1846 | currentSection = (Doc::Sections) (unit); |
1847 | priv->constructExtra(); |
1848 | priv->extra->section = currentSection; |
1849 | } |
1850 | else |
1851 | endSection(unit,cmd); |
1852 | |
1853 | append(Atom::SectionLeft, QString::number(unit)); |
1854 | priv->constructExtra(); |
1855 | priv->extra->tableOfContents.append(priv->text.lastAtom()); |
1856 | priv->extra->tableOfContentsLevels.append(unit); |
1857 | enterPara(Atom::SectionHeadingLeft, |
1858 | Atom::SectionHeadingRight, |
1859 | QString::number(unit)); |
1860 | currentSection = unit; |
1861 | |
1862 | } |
1863 | |
1864 | void DocParser::endSection(int , int) // (int unit, int endCmd) |
1865 | { |
1866 | leavePara(); |
1867 | append(Atom::SectionRight, QString::number(currentSection)); |
1868 | currentSection = (Doc::NoSection); |
1869 | } |
1870 | |
1871 | void DocParser::parseAlso() |
1872 | { |
1873 | leavePara(); |
1874 | skipSpacesOnLine(); |
1875 | while (pos < len && in[pos] != |
1876 | QString target; |
1877 | QString str; |
1878 | |
1879 | if (in[pos] == |
1880 | target = getArgument(); |
1881 | skipSpacesOnLine(); |
1882 | if (in[pos] == |
1883 | str = getArgument(); |
1884 | |
1885 | // hack for C++ to support links like \l{QString::}{count()} |
1886 | if (target.endsWith("::")) |
1887 | target += str; |
1888 | } |
1889 | else { |
1890 | str = target; |
1891 | } |
1892 | #ifdef QDOC2_COMPAT |
1893 | } |
1894 | else if (in[pos] == "\\link") { |
1895 | pos += 6; |
1896 | target = getArgument(); |
1897 | int endPos = in.indexOf("\\endlink", pos); |
1898 | if (endPos != -1) { |
1899 | str = in.mid(pos, endPos - pos).trimmed(); |
1900 | pos = endPos + 8; |
1901 | } |
1902 | #endif |
1903 | } |
1904 | else { |
1905 | target = getArgument(); |
1906 | str = cleanLink(target); |
1907 | } |
1908 | |
1909 | Text also; |
1910 | also << Atom(Atom::Link, target) |
1911 | << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) |
1912 | << str |
1913 | << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); |
1914 | priv->addAlso(also); |
1915 | |
1916 | skipSpacesOnLine(); |
1917 | if (pos < len && in[pos] == |
1918 | pos++; |
1919 | skipSpacesOrOneEndl(); |
1920 | } |
1921 | else if (in[pos] != |
1922 | location().warning(tr("Missing comma in '\\%1'").arg(cmdName(CMD_SA))); |
1923 | } |
1924 | } |
1925 | } |
1926 | |
1927 | //static bool debug = false; |
1928 | #if 0 |
1929 | if (type == Atom::DivLeft) |
1930 | debug = true; |
1931 | if (debug) |
1932 | qDebug() << type << string; |
1933 | if (type == Atom::DivRight) |
1934 | debug = false; |
1935 | #endif |
1936 | |
1937 | void DocParser::append(Atom::Type type, const QString &string) |
1938 | { |
1939 | Atom::Type lastType = priv->text.lastAtom()->type(); |
1940 | if ((lastType == Atom::Code) && priv->text.lastAtom()->string().endsWith(QLatin1String("\n\n"))) |
1941 | priv->text.lastAtom()->chopString(); |
1942 | priv->text << Atom(type, string); |
1943 | } |
1944 | |
1945 | void DocParser::append(Atom::Type type, const QString& p1, const QString& p2) |
1946 | { |
1947 | Atom::Type lastType = priv->text.lastAtom()->type(); |
1948 | if ((lastType == Atom::Code) && priv->text.lastAtom()->string().endsWith(QLatin1String("\n\n"))) |
1949 | priv->text.lastAtom()->chopString(); |
1950 | priv->text << Atom(type, p1, p2); |
1951 | } |
1952 | |
1953 | void DocParser::appendChar(QChar ch) |
1954 | { |
1955 | if (priv->text.lastAtom()->type() != Atom::String) |
1956 | append(Atom::String); |
1957 | Atom *atom = priv->text.lastAtom(); |
1958 | if (ch == QLatin1Char( |
1959 | if (!atom->string().endsWith(QLatin1Char( |
1960 | atom->appendChar(QLatin1Char( |
1961 | } |
1962 | else |
1963 | atom->appendChar(ch); |
1964 | } |
1965 | |
1966 | void DocParser::appendWord(const QString &word) |
1967 | { |
1968 | if (priv->text.lastAtom()->type() != Atom::String) { |
1969 | append(Atom::String, word); |
1970 | } |
1971 | else |
1972 | priv->text.lastAtom()->appendString(word); |
1973 | } |
1974 | |
1975 | void DocParser::appendToCode(const QString& markedCode) |
1976 | { |
1977 | Atom::Type lastType = priv->text.lastAtom()->type(); |
1978 | #ifdef QDOC_QML |
1979 | if (lastType != Atom::Qml && lastType != Atom::Code && lastType != Atom::JavaScript) |
1980 | append(Atom::Qml); |
1981 | #else |
1982 | if (lastType != Atom::Code) |
1983 | append(Atom::Code); |
1984 | #endif |
1985 | priv->text.lastAtom()->appendString(markedCode); |
1986 | } |
1987 | |
1988 | void DocParser::appendToCode(const QString &markedCode, Atom::Type defaultType) |
1989 | { |
1990 | Atom::Type lastType = priv->text.lastAtom()->type(); |
1991 | if (lastType != Atom::Qml && lastType != Atom::Code && lastType != Atom::JavaScript) |
1992 | append(defaultType, markedCode); |
1993 | else |
1994 | priv->text.lastAtom()->appendString(markedCode); |
1995 | } |
1996 | |
1997 | void DocParser::startNewPara() |
1998 | { |
1999 | leavePara(); |
2000 | enterPara(); |
2001 | } |
2002 | |
2003 | void DocParser::enterPara(Atom::Type leftType, |
2004 | Atom::Type rightType, |
2005 | const QString& string) |
2006 | { |
2007 | if (paraState == OutsideParagraph) { |
2008 | |
2009 | if ((priv->text.lastAtom()->type() != Atom::ListItemLeft) && |
2010 | (priv->text.lastAtom()->type() != Atom::DivLeft)) { |
2011 | leaveValueList(); |
2012 | } |
2013 | |
2014 | append(leftType, string); |
2015 | indexStartedPara = false; |
2016 | pendingParaLeftType = leftType; |
2017 | pendingParaRightType = rightType; |
2018 | pendingParaString = string; |
2019 | if (leftType == Atom::SectionHeadingLeft) { |
2020 | paraState = InSingleLineParagraph; |
2021 | } |
2022 | else { |
2023 | paraState = InMultiLineParagraph; |
2024 | } |
2025 | skipSpacesOrOneEndl(); |
2026 | } |
2027 | } |
2028 | |
2029 | void DocParser::leavePara() |
2030 | { |
2031 | if (paraState != OutsideParagraph) { |
2032 | if (!pendingFormats.isEmpty()) { |
2033 | location().warning(tr("Missing '}'")); |
2034 | pendingFormats.clear(); |
2035 | } |
2036 | |
2037 | if (priv->text.lastAtom()->type() == pendingParaLeftType) { |
2038 | priv->text.stripLastAtom(); |
2039 | } |
2040 | else { |
2041 | if (priv->text.lastAtom()->type() == Atom::String && |
2042 | priv->text.lastAtom()->string().endsWith(" ")) { |
2043 | priv->text.lastAtom()->chopString(); |
2044 | } |
2045 | append(pendingParaRightType, pendingParaString); |
2046 | } |
2047 | paraState = OutsideParagraph; |
2048 | indexStartedPara = false; |
2049 | pendingParaRightType = Atom::Nop; |
2050 | pendingParaString = ""; |
2051 | } |
2052 | } |
2053 | |
2054 | void DocParser::leaveValue() |
2055 | { |
2056 | leavePara(); |
2057 | if (openedLists.isEmpty()) { |
2058 | openedLists.push(OpenedList(OpenedList::Value)); |
2059 | append(Atom::ListLeft, ATOM_LIST_VALUE); |
2060 | } |
2061 | else { |
2062 | if (priv->text.lastAtom()->type() == Atom::Nop) |
2063 | priv->text.stripLastAtom(); |
2064 | append(Atom::ListItemRight, ATOM_LIST_VALUE); |
2065 | } |
2066 | } |
2067 | |
2068 | void DocParser::leaveValueList() |
2069 | { |
2070 | leavePara(); |
2071 | if (!openedLists.isEmpty() && |
2072 | (openedLists.top().style() == OpenedList::Value)) { |
2073 | if (priv->text.lastAtom()->type() == Atom::Nop) |
2074 | priv->text.stripLastAtom(); |
2075 | append(Atom::ListItemRight, ATOM_LIST_VALUE); |
2076 | append(Atom::ListRight, ATOM_LIST_VALUE); |
2077 | openedLists.pop(); |
2078 | } |
2079 | } |
2080 | |
2081 | void DocParser::leaveTableRow() |
2082 | { |
2083 | if (inTableItem) { |
2084 | leavePara(); |
2085 | append(Atom::TableItemRight); |
2086 | inTableItem = false; |
2087 | } |
2088 | if (inTableHeader) { |
2089 | append(Atom::TableHeaderRight); |
2090 | inTableHeader = false; |
2091 | } |
2092 | if (inTableRow) { |
2093 | append(Atom::TableRowRight); |
2094 | inTableRow = false; |
2095 | } |
2096 | } |
2097 | |
2098 | CodeMarker *DocParser::quoteFromFile() |
2099 | { |
2100 | return Doc::quoteFromFile(location(), quoter, getArgument()); |
2101 | } |
2102 | |
2103 | void DocParser::expandMacro(const QString &name, |
2104 | const QString &def, |
2105 | int numParams) |
2106 | { |
2107 | if (numParams == 0) { |
2108 | append(Atom::RawString, def); |
2109 | } |
2110 | else { |
2111 | QStringList args; |
2112 | QString rawString; |
2113 | |
2114 | for (int i = 0; i < numParams; i++) { |
2115 | if (numParams == 1 || isLeftBraceAhead()) { |
2116 | args << getArgument(true); |
2117 | } |
2118 | else { |
2119 | location().warning(tr("Macro '\\%1' invoked with too few" |
2120 | " arguments (expected %2, got %3)") |
2121 | .arg(name).arg(numParams).arg(i)); |
2122 | break; |
2123 | } |
2124 | } |
2125 | |
2126 | int j = 0; |
2127 | while (j < def.size()) { |
2128 | int paramNo; |
2129 | if (((paramNo = def[j].unicode()) >= 1) && |
2130 | (paramNo <= numParams)) { |
2131 | if (!rawString.isEmpty()) { |
2132 | append(Atom::RawString, rawString); |
2133 | rawString = ""; |
2134 | } |
2135 | append(Atom::String, args[paramNo - 1]); |
2136 | j += 1; |
2137 | } |
2138 | else { |
2139 | rawString += def[j++]; |
2140 | } |
2141 | } |
2142 | if (!rawString.isEmpty()) |
2143 | append(Atom::RawString, rawString); |
2144 | } |
2145 | } |
2146 | |
2147 | QString DocParser::expandMacroToString(const QString &name, const QString &def, int numParams) |
2148 | { |
2149 | if (numParams == 0) { |
2150 | return def; |
2151 | } |
2152 | else { |
2153 | QStringList args; |
2154 | QString rawString; |
2155 | |
2156 | for (int i = 0; i < numParams; i++) { |
2157 | if (numParams == 1 || isLeftBraceAhead()) { |
2158 | args << getArgument(true); |
2159 | } |
2160 | else { |
2161 | location().warning(tr("Macro '\\%1' invoked with too few" |
2162 | " arguments (expected %2, got %3)") |
2163 | .arg(name).arg(numParams).arg(i)); |
2164 | break; |
2165 | } |
2166 | } |
2167 | |
2168 | int j = 0; |
2169 | while (j < def.size()) { |
2170 | int paramNo; |
2171 | if (((paramNo = def[j].unicode()) >= 1) && |
2172 | (paramNo <= numParams)) { |
2173 | rawString += args[paramNo - 1]; |
2174 | j += 1; |
2175 | } |
2176 | else { |
2177 | rawString += def[j++]; |
2178 | } |
2179 | } |
2180 | return rawString; |
2181 | } |
2182 | } |
2183 | |
2184 | Doc::Sections DocParser::getSectioningUnit() |
2185 | { |
2186 | QString name = getOptionalArgument(); |
2187 | |
2188 | if (name == "part") { |
2189 | return Doc::Part; |
2190 | } |
2191 | else if (name == "chapter") { |
2192 | return Doc::Chapter; |
2193 | } |
2194 | else if (name == "section1") { |
2195 | return Doc::Section1; |
2196 | } |
2197 | else if (name == "section2") { |
2198 | return Doc::Section2; |
2199 | } |
2200 | else if (name == "section3") { |
2201 | return Doc::Section3; |
2202 | } |
2203 | else if (name == "section4") { |
2204 | return Doc::Section4; |
2205 | } |
2206 | else if (name.isEmpty()) { |
2207 | return Doc::NoSection; |
2208 | } |
2209 | else { |
2210 | location().warning(tr("Invalid section '%1'").arg(name)); |
2211 | return Doc::NoSection; |
2212 | } |
2213 | } |
2214 | |
2215 | QString DocParser::getArgument(bool verbatim) |
2216 | { |
2217 | QString arg; |
2218 | int delimDepth = 0; |
2219 | |
2220 | skipSpacesOrOneEndl(); |
2221 | |
2222 | int startPos = pos; |
2223 | |
2224 | /* |
2225 | Typically, an argument ends at the next white-space. However, |
2226 | braces can be used to group words: |
2227 | |
2228 | {a few words} |
2229 | |
2230 | Also, opening and closing parentheses have to match. Thus, |
2231 | |
2232 | printf("%d\n", x) |
2233 | |
2234 | is an argument too, although it contains spaces. Finally, |
2235 | trailing punctuation is not included in an argument, nor is 's. |
2236 | */ |
2237 | if (pos < (int) in.length() && in[pos] == |
2238 | pos++; |
2239 | while (pos < (int) in.length() && delimDepth >= 0) { |
2240 | switch (in[pos].unicode()) { |
2241 | case |
2242 | delimDepth++; |
2243 | arg += "{"; |
2244 | pos++; |
2245 | break; |
2246 | case |
2247 | delimDepth--; |
2248 | if (delimDepth >= 0) |
2249 | arg += "}"; |
2250 | pos++; |
2251 | break; |
2252 | case |
2253 | if (verbatim) { |
2254 | arg += in[pos]; |
2255 | pos++; |
2256 | } |
2257 | else { |
2258 | pos++; |
2259 | if (pos < (int) in.length()) { |
2260 | if (in[pos].isLetterOrNumber()) |
2261 | break; |
2262 | arg += in[pos]; |
2263 | if (in[pos].isSpace()) { |
2264 | skipAllSpaces(); |
2265 | } |
2266 | else { |
2267 | pos++; |
2268 | } |
2269 | } |
2270 | } |
2271 | break; |
2272 | default: |
2273 | arg += in[pos]; |
2274 | pos++; |
2275 | } |
2276 | } |
2277 | if (delimDepth > 0) |
2278 | location().warning(tr("Missing '}'")); |
2279 | } |
2280 | else { |
2281 | while ((pos < in.length()) && |
2282 | ((delimDepth > 0) || ((delimDepth == 0) && !in[pos].isSpace()))) { |
2283 | switch (in[pos].unicode()) { |
2284 | case |
2285 | case |
2286 | case |
2287 | delimDepth++; |
2288 | arg += in[pos]; |
2289 | pos++; |
2290 | break; |
2291 | case |
2292 | case |
2293 | case |
2294 | delimDepth--; |
2295 | if (pos == startPos || delimDepth >= 0) { |
2296 | arg += in[pos]; |
2297 | pos++; |
2298 | } |
2299 | break; |
2300 | case |
2301 | if (verbatim) { |
2302 | arg += in[pos]; |
2303 | pos++; |
2304 | } |
2305 | else { |
2306 | pos++; |
2307 | if (pos < (int) in.length()) { |
2308 | if (in[pos].isLetterOrNumber()) |
2309 | break; |
2310 | arg += in[pos]; |
2311 | if (in[pos].isSpace()) { |
2312 | skipAllSpaces(); |
2313 | } |
2314 | else { |
2315 | pos++; |
2316 | } |
2317 | } |
2318 | } |
2319 | break; |
2320 | default: |
2321 | arg += in[pos]; |
2322 | pos++; |
2323 | } |
2324 | } |
2325 | if ((arg.length() > 1) && |
2326 | (QString(".,:;!?").indexOf(in[pos - 1]) != -1) && |
2327 | !arg.endsWith("...")) { |
2328 | arg.truncate(arg.length() - 1); |
2329 | pos--; |
2330 | } |
2331 | if (arg.length() > 2 && in.mid(pos - 2, 2) == "'s") { |
2332 | arg.truncate(arg.length() - 2); |
2333 | pos -= 2; |
2334 | } |
2335 | } |
2336 | return arg.simplified(); |
2337 | } |
2338 | |
2339 | QString DocParser::getOptionalArgument() |
2340 | { |
2341 | skipSpacesOrOneEndl(); |
2342 | if (pos + 1 < (int) in.length() && in[pos] == |
2343 | in[pos + 1].isLetterOrNumber()) { |
2344 | return ""; |
2345 | } |
2346 | else { |
2347 | return getArgument(); |
2348 | } |
2349 | } |
2350 | |
2351 | QString DocParser::getRestOfLine() |
2352 | { |
2353 | QString t; |
2354 | |
2355 | skipSpacesOnLine(); |
2356 | |
2357 | bool trailingSlash = false; |
2358 | |
2359 | do { |
2360 | int begin = pos; |
2361 | |
2362 | while (pos < in.size() && in[pos] != |
2363 | if (in[pos] == |
2364 | trailingSlash = true; |
2365 | ++pos; |
2366 | while ((pos < in.size()) && |
2367 | in[pos].isSpace() && |
2368 | (in[pos] != |
2369 | ++pos; |
2370 | } |
2371 | else { |
2372 | trailingSlash = false; |
2373 | ++pos; |
2374 | } |
2375 | } |
2376 | |
2377 | if (!t.isEmpty()) |
2378 | t += " "; |
2379 | t += in.mid(begin, pos - begin).simplified(); |
2380 | |
2381 | if (trailingSlash) { |
2382 | t.chop(1); |
2383 | t = t.simplified(); |
2384 | } |
2385 | if (pos < in.size()) |
2386 | ++pos; |
2387 | } while (pos < in.size() && trailingSlash); |
2388 | |
2389 | return t; |
2390 | } |
2391 | |
2392 | /*! |
2393 | The metacommand argument is normally the remaining text to |
2394 | the right of the metacommand itself. The extra blanks are |
2395 | stripped and the argument string is returned. |
2396 | */ |
2397 | QString DocParser::getMetaCommandArgument(const QString &cmdStr) |
2398 | { |
2399 | skipSpacesOnLine(); |
2400 | |
2401 | int begin = pos; |
2402 | int parenDepth = 0; |
2403 | |
2404 | while (pos < in.size() && (in[pos] != |
2405 | if (in.at(pos) == |
2406 | ++parenDepth; |
2407 | else if (in.at(pos) == |
2408 | --parenDepth; |
2409 | |
2410 | ++pos; |
2411 | } |
2412 | if (pos == in.size() && parenDepth > 0) { |
2413 | pos = begin; |
2414 | location().warning(tr("Unbalanced parentheses in '%1'").arg(cmdStr)); |
2415 | } |
2416 | |
2417 | QString t = in.mid(begin, pos - begin).simplified(); |
2418 | skipSpacesOnLine(); |
2419 | return t; |
2420 | } |
2421 | |
2422 | QString DocParser::getUntilEnd(int cmd) |
2423 | { |
2424 | int endCmd = endCmdFor(cmd); |
2425 | QRegExp rx("\\\\"+ cmdName(endCmd) + "\\b"); |
2426 | QString t; |
2427 | int end = rx.indexIn(in, pos); |
2428 | |
2429 | if (end == -1) { |
2430 | location().warning(tr("Missing '\\%1'").arg(cmdName(endCmd))); |
2431 | pos = in.length(); |
2432 | } |
2433 | else { |
2434 | t = in.mid(pos, end - pos); |
2435 | pos = end + rx.matchedLength(); |
2436 | } |
2437 | return t; |
2438 | } |
2439 | |
2440 | QString DocParser::getCode(int cmd, CodeMarker *marker) |
2441 | { |
2442 | QString code = untabifyEtc(getUntilEnd(cmd)); |
2443 | int indent = indentLevel(code); |
2444 | if (indent < minIndent) |
2445 | minIndent = indent; |
2446 | code = unindent(minIndent, code); |
2447 | if (!marker) |
2448 | marker = CodeMarker::markerForCode(code); |
2449 | return marker->markedUpCode(code, 0, location()); |
2450 | } |
2451 | |
2452 | /*! |
2453 | Was used only for generating doxygen output. |
2454 | */ |
2455 | QString DocParser::getUnmarkedCode(int cmd) |
2456 | { |
2457 | QString code = getUntilEnd(cmd); |
2458 | return code; |
2459 | } |
2460 | |
2461 | bool DocParser::isBlankLine() |
2462 | { |
2463 | int i = pos; |
2464 | |
2465 | while (i < len && in[i].isSpace()) { |
2466 | if (in[i] == |
2467 | return true; |
2468 | i++; |
2469 | } |
2470 | return false; |
2471 | } |
2472 | |
2473 | bool DocParser::isLeftBraceAhead() |
2474 | { |
2475 | int numEndl = 0; |
2476 | int i = pos; |
2477 | |
2478 | while (i < len && in[i].isSpace() && numEndl < 2) { |
2479 | // ### bug with '\\' |
2480 | if (in[i] == |
2481 | numEndl++; |
2482 | i++; |
2483 | } |
2484 | return numEndl < 2 && i < len && in[i] == |
2485 | } |
2486 | |
2487 | /*! |
2488 | Skips to the next non-space character or EOL. |
2489 | */ |
2490 | void DocParser::skipSpacesOnLine() |
2491 | { |
2492 | while ((pos < in.length()) && |
2493 | in[pos].isSpace() && |
2494 | (in[pos].unicode() != |
2495 | ++pos; |
2496 | } |
2497 | |
2498 | /*! |
2499 | Skips spaces and on EOL. |
2500 | */ |
2501 | void DocParser::skipSpacesOrOneEndl() |
2502 | { |
2503 | int firstEndl = -1; |
2504 | while (pos < (int) in.length() && in[pos].isSpace()) { |
2505 | QChar ch = in[pos]; |
2506 | if (ch == |
2507 | if (firstEndl == -1) { |
2508 | firstEndl = pos; |
2509 | } |
2510 | else { |
2511 | pos = firstEndl; |
2512 | break; |
2513 | } |
2514 | } |
2515 | pos++; |
2516 | } |
2517 | } |
2518 | |
2519 | void DocParser::skipAllSpaces() |
2520 | { |
2521 | while (pos < len && in[pos].isSpace()) |
2522 | pos++; |
2523 | } |
2524 | |
2525 | void DocParser::skipToNextPreprocessorCommand() |
2526 | { |
2527 | QRegExp rx("\\\\(?:"+ cmdName(CMD_IF) + "|"+ |
2528 | cmdName(CMD_ELSE) + "|"+ |
2529 | cmdName(CMD_ENDIF) + ")\\b"); |
2530 | int end = rx.indexIn(in, pos + 1); // ### + 1 necessary? |
2531 | |
2532 | if (end == -1) |
2533 | pos = in.length(); |
2534 | else |
2535 | pos = end; |
2536 | } |
2537 | |
2538 | int DocParser::endCmdFor(int cmd) |
2539 | { |
2540 | switch (cmd) { |
2541 | case CMD_ABSTRACT: |
2542 | return CMD_ENDABSTRACT; |
2543 | case CMD_BADCODE: |
2544 | return CMD_ENDCODE; |
2545 | case CMD_CHAPTER: |
2546 | return CMD_ENDCHAPTER; |
2547 | case CMD_CODE: |
2548 | return CMD_ENDCODE; |
2549 | case CMD_DIV: |
2550 | return CMD_ENDDIV; |
2551 | case CMD_QML: |
2552 | return CMD_ENDQML; |
2553 | case CMD_QMLTEXT: |
2554 | return CMD_ENDQMLTEXT; |
2555 | case CMD_JS: |
2556 | return CMD_ENDJS; |
2557 | case CMD_FOOTNOTE: |
2558 | return CMD_ENDFOOTNOTE; |
2559 | case CMD_LEGALESE: |
2560 | return CMD_ENDLEGALESE; |
2561 | case CMD_LINK: |
2562 | return CMD_ENDLINK; |
2563 | case CMD_LIST: |
2564 | return CMD_ENDLIST; |
2565 | case CMD_NEWCODE: |
2566 | return CMD_ENDCODE; |
2567 | case CMD_OLDCODE: |
2568 | return CMD_NEWCODE; |
2569 | case CMD_OMIT: |
2570 | return CMD_ENDOMIT; |
2571 | case CMD_PART: |
2572 | return CMD_ENDPART; |
2573 | case CMD_QUOTATION: |
2574 | return CMD_ENDQUOTATION; |
2575 | case CMD_RAW: |
2576 | return CMD_ENDRAW; |
2577 | case CMD_SECTION1: |
2578 | return CMD_ENDSECTION1; |
2579 | case CMD_SECTION2: |
2580 | return CMD_ENDSECTION2; |
2581 | case CMD_SECTION3: |
2582 | return CMD_ENDSECTION3; |
2583 | case CMD_SECTION4: |
2584 | return CMD_ENDSECTION4; |
2585 | case CMD_SIDEBAR: |
2586 | return CMD_ENDSIDEBAR; |
2587 | case CMD_TABLE: |
2588 | return CMD_ENDTABLE; |
2589 | default: |
2590 | return cmd; |
2591 | } |
2592 | } |
2593 | |
2594 | QString DocParser::cmdName(int cmd) |
2595 | { |
2596 | return *cmds[cmd].alias; |
2597 | } |
2598 | |
2599 | QString DocParser::endCmdName(int cmd) |
2600 | { |
2601 | return cmdName(endCmdFor(cmd)); |
2602 | } |
2603 | |
2604 | QString DocParser::untabifyEtc(const QString& str) |
2605 | { |
2606 | QString result; |
2607 | result.reserve(str.length()); |
2608 | int column = 0; |
2609 | |
2610 | for (int i = 0; i < str.length(); i++) { |
2611 | const QChar c = str.at(i); |
2612 | if (c == QLatin1Char( |
2613 | continue; |
2614 | if (c == QLatin1Char( |
2615 | result += " "+ (column % tabSize); |
2616 | column = ((column / tabSize) + 1) * tabSize; |
2617 | continue; |
2618 | } |
2619 | if (c == QLatin1Char( |
2620 | while (result.endsWith(QLatin1Char( |
2621 | result.chop(1); |
2622 | result += c; |
2623 | column = 0; |
2624 | continue; |
2625 | } |
2626 | result += c; |
2627 | column++; |
2628 | } |
2629 | |
2630 | while (result.endsWith("\n\n")) |
2631 | result.truncate(result.length() - 1); |
2632 | while (result.startsWith("\n")) |
2633 | result = result.mid(1); |
2634 | |
2635 | return result; |
2636 | } |
2637 | |
2638 | int DocParser::indentLevel(const QString& str) |
2639 | { |
2640 | int minIndent = INT_MAX; |
2641 | int column = 0; |
2642 | |
2643 | for (int i = 0; i < (int) str.length(); i++) { |
2644 | if (str[i] == |
2645 | column = 0; |
2646 | } |
2647 | else { |
2648 | if (str[i] != |
2649 | minIndent = column; |
2650 | column++; |
2651 | } |
2652 | } |
2653 | return minIndent; |
2654 | } |
2655 | |
2656 | QString DocParser::unindent(int level, const QString& str) |
2657 | { |
2658 | if (level == 0) |
2659 | return str; |
2660 | |
2661 | QString t; |
2662 | int column = 0; |
2663 | |
2664 | for (int i = 0; i < (int) str.length(); i++) { |
2665 | if (str[i] == QLatin1Char( |
2666 | t += |
2667 | column = 0; |
2668 | } |
2669 | else { |
2670 | if (column >= level) |
2671 | t += str[i]; |
2672 | column++; |
2673 | } |
2674 | } |
2675 | return t; |
2676 | } |
2677 | |
2678 | QString DocParser::slashed(const QString& str) |
2679 | { |
2680 | QString result = str; |
2681 | result.replace("/", "\\/"); |
2682 | return "/"+ result + "/"; |
2683 | } |
2684 | |
2685 | #define COMMAND_BRIEF Doc::alias("brief") |
2686 | #define COMMAND_QMLBRIEF Doc::alias("qmlbrief") |
2687 | |
2688 | Doc::Doc(const Location& start_loc, |
2689 | const Location& end_loc, |
2690 | const QString& source, |
2691 | const QSet<QString>& metaCommandSet) |
2692 | { |
2693 | priv = new DocPrivate(start_loc,end_loc,source); |
2694 | DocParser parser; |
2695 | parser.parse(source,priv,metaCommandSet); |
2696 | } |
2697 | |
2698 | Doc::Doc(const Doc& doc) |
2699 | : priv(0) |
2700 | { |
2701 | operator=(doc); |
2702 | } |
2703 | |
2704 | Doc::~Doc() |
2705 | { |
2706 | if (priv && priv->deref()) |
2707 | delete priv; |
2708 | } |
2709 | |
2710 | Doc &Doc::operator=(const Doc& doc) |
2711 | { |
2712 | if (doc.priv) |
2713 | doc.priv->ref(); |
2714 | if (priv && priv->deref()) |
2715 | delete priv; |
2716 | priv = doc.priv; |
2717 | return *this; |
2718 | } |
2719 | |
2720 | void Doc::renameParameters(const QStringList &oldNames, |
2721 | const QStringList &newNames) |
2722 | { |
2723 | if (priv && oldNames != newNames) { |
2724 | detach(); |
2725 | |
2726 | priv->params = newNames.toSet(); |
2727 | |
2728 | Atom *atom = priv->text.firstAtom(); |
2729 | while (atom) { |
2730 | if (atom->type() == Atom::FormattingLeft |
2731 | && atom->string() == ATOM_FORMATTING_PARAMETER) { |
2732 | atom = atom->next(); |
2733 | if (!atom) |
2734 | return; |
2735 | int index = oldNames.indexOf(atom->string()); |
2736 | if (index != -1 && index < newNames.count()) |
2737 | atom->setString(newNames.at(index)); |
2738 | } |
2739 | atom = atom->next(); |
2740 | } |
2741 | } |
2742 | } |
2743 | |
2744 | void Doc::simplifyEnumDoc() |
2745 | { |
2746 | if (priv) { |
2747 | if (priv->isEnumDocSimplifiable()) { |
2748 | detach(); |
2749 | |
2750 | Text newText; |
2751 | |
2752 | Atom *atom = priv->text.firstAtom(); |
2753 | while (atom) { |
2754 | if ((atom->type() == Atom::ListLeft) && |
2755 | (atom->string() == ATOM_LIST_VALUE)) { |
2756 | while (atom && ((atom->type() != Atom::ListRight) || |
2757 | (atom->string() != ATOM_LIST_VALUE))) |
2758 | atom = atom->next(); |
2759 | if (atom) |
2760 | atom = atom->next(); |
2761 | } |
2762 | else { |
2763 | newText << *atom; |
2764 | atom = atom->next(); |
2765 | } |
2766 | } |
2767 | priv->text = newText; |
2768 | } |
2769 | } |
2770 | } |
2771 | |
2772 | void Doc::setBody(const Text &text) |
2773 | { |
2774 | detach(); |
2775 | priv->text = text; |
2776 | } |
2777 | |
2778 | /*! |
2779 | Returns the starting location of a qdoc comment. |
2780 | */ |
2781 | const Location &Doc::location() const |
2782 | { |
2783 | static const Location dummy; |
2784 | return priv == 0 ? dummy : priv->start_loc; |
2785 | } |
2786 | |
2787 | const QString &Doc::source() const |
2788 | { |
2789 | static QString null; |
2790 | return priv == 0 ? null : priv->src; |
2791 | } |
2792 | |
2793 | bool Doc::isEmpty() const |
2794 | { |
2795 | return priv == 0 || priv->src.isEmpty(); |
2796 | } |
2797 | |
2798 | const Text& Doc::body() const |
2799 | { |
2800 | static const Text dummy; |
2801 | return priv == 0 ? dummy : priv->text; |
2802 | } |
2803 | |
2804 | Text Doc::briefText(bool inclusive) const |
2805 | { |
2806 | return body().subText(Atom::BriefLeft, Atom::BriefRight, 0, inclusive); |
2807 | } |
2808 | |
2809 | Text Doc::trimmedBriefText(const QString &className) const |
2810 | { |
2811 | QString classNameOnly = className; |
2812 | if (className.contains("::")) |
2813 | classNameOnly = className.split("::").last(); |
2814 | |
2815 | Text originalText = briefText(); |
2816 | Text resultText; |
2817 | const Atom *atom = originalText.firstAtom(); |
2818 | if (atom) { |
2819 | QString briefStr; |
2820 | QString whats; |
2821 | bool standardWording = true; |
2822 | |
2823 | /* |
2824 | This code is really ugly. The entire \brief business |
2825 | should be rethought. |
2826 | */ |
2827 | while (atom) { |
2828 | if (atom->type() == Atom::AutoLink || atom->type() == Atom::String) { |
2829 | briefStr += atom->string(); |
2830 | } |
2831 | atom = atom->next(); |
2832 | } |
2833 | |
2834 | QStringList w = briefStr.split(" "); |
2835 | if (!w.isEmpty() && w.first() == "Returns") { |
2836 | } |
2837 | else { |
2838 | if (!w.isEmpty() && w.first() == "The") |
2839 | w.removeFirst(); |
2840 | else { |
2841 | location().warning( |
2842 | tr("Nonstandard wording in '\\%1' text for '%2' (expected 'The')") |
2843 | .arg(COMMAND_BRIEF).arg(className)); |
2844 | standardWording = false; |
2845 | } |
2846 | |
2847 | if (!w.isEmpty() && (w.first() == className || w.first() == classNameOnly)) |
2848 | w.removeFirst(); |
2849 | else { |
2850 | location().warning( |
2851 | tr("Nonstandard wording in '\\%1' text for '%2' (expected '%3')") |
2852 | .arg(COMMAND_BRIEF).arg(className).arg(className)); |
2853 | standardWording = false; |
2854 | } |
2855 | |
2856 | if (!w.isEmpty() && ((w.first() == "class") || |
2857 | (w.first() == "function") || |
2858 | (w.first() == "macro") || |
2859 | (w.first() == "widget") || |
2860 | (w.first() == "namespace") || |
2861 | (w.first() == "header"))) |
2862 | w.removeFirst(); |
2863 | else { |
2864 | location().warning( |
2865 | tr("Nonstandard wording in '\\%1' text for '%2' (" |
2866 | "expected 'class', 'function', 'macro', 'widget', " |
2867 | "'namespace' or 'header')") |
2868 | .arg(COMMAND_BRIEF).arg(className)); |
2869 | standardWording = false; |
2870 | } |
2871 | |
2872 | if (!w.isEmpty() && (w.first() == "is"|| w.first() == "provides")) |
2873 | w.removeFirst(); |
2874 | |
2875 | if (!w.isEmpty() && (w.first() == "a"|| w.first() == "an")) |
2876 | w.removeFirst(); |
2877 | } |
2878 | |
2879 | whats = w.join(" "); |
2880 | |
2881 | if (whats.endsWith(".")) |
2882 | whats.truncate(whats.length() - 1); |
2883 | |
2884 | if (whats.isEmpty()) { |
2885 | location().warning( |
2886 | tr("Nonstandard wording in '\\%1' text for '%2' (expected more text)") |
2887 | .arg(COMMAND_BRIEF).arg(className)); |
2888 | standardWording = false; |
2889 | } |
2890 | else |
2891 | whats[0] = whats[0].toUpper(); |
2892 | |
2893 | // ### move this once \brief is abolished for properties |
2894 | if (standardWording) |
2895 | resultText << whats; |
2896 | } |
2897 | return resultText; |
2898 | } |
2899 | |
2900 | Text Doc::legaleseText() const |
2901 | { |
2902 | if (priv == 0 || !priv->hasLegalese) |
2903 | return Text(); |
2904 | else |
2905 | return body().subText(Atom::LegaleseLeft, Atom::LegaleseRight); |
2906 | } |
2907 | |
2908 | const QString& Doc::baseName() const |
2909 | { |
2910 | static QString null; |
2911 | if (priv == 0 || priv->extra == 0) { |
2912 | return null; |
2913 | } |
2914 | else { |
2915 | return priv->extra->baseName; |
2916 | } |
2917 | } |
2918 | |
2919 | Doc::Sections Doc::granularity() const |
2920 | { |
2921 | if (priv == 0 || priv->extra == 0) { |
2922 | return DocPrivateExtra().granularity; |
2923 | } |
2924 | else { |
2925 | return priv->extra->granularity; |
2926 | } |
2927 | } |
2928 | |
2929 | const QSet<QString> &Doc::parameterNames() const |
2930 | { |
2931 | return priv == 0 ? *null_Set_QString() : priv->params; |
2932 | } |
2933 | |
2934 | const QStringList &Doc::enumItemNames() const |
2935 | { |
2936 | return priv == 0 ? *null_QStringList() : priv->enumItemList; |
2937 | } |
2938 | |
2939 | const QStringList &Doc::omitEnumItemNames() const |
2940 | { |
2941 | return priv == 0 ? *null_QStringList() : priv->omitEnumItemList; |
2942 | } |
2943 | |
2944 | const QSet<QString> &Doc::metaCommandsUsed() const |
2945 | { |
2946 | return priv == 0 ? *null_Set_QString() : priv->metacommandsUsed; |
2947 | } |
2948 | |
2949 | QStringList Doc::metaCommandArgs(const QString& metacommand) const |
2950 | { |
2951 | return priv == 0 ? QStringList() : priv->metaCommandMap.value(metacommand); |
2952 | } |
2953 | |
2954 | const QList<Text> &Doc::alsoList() const |
2955 | { |
2956 | return priv == 0 ? *null_QList_Text() : priv->alsoList; |
2957 | } |
2958 | |
2959 | bool Doc::hasTableOfContents() const |
2960 | { |
2961 | return priv && priv->extra && !priv->extra->tableOfContents.isEmpty(); |
2962 | } |
2963 | |
2964 | bool Doc::hasKeywords() const |
2965 | { |
2966 | return priv && priv->extra && !priv->extra->keywords.isEmpty(); |
2967 | } |
2968 | |
2969 | bool Doc::hasTargets() const |
2970 | { |
2971 | return priv && priv->extra && !priv->extra->targets.isEmpty(); |
2972 | } |
2973 | |
2974 | const QList<Atom *> &Doc::tableOfContents() const |
2975 | { |
2976 | priv->constructExtra(); |
2977 | return priv->extra->tableOfContents; |
2978 | } |
2979 | |
2980 | const QList<int> &Doc::tableOfContentsLevels() const |
2981 | { |
2982 | priv->constructExtra(); |
2983 | return priv->extra->tableOfContentsLevels; |
2984 | } |
2985 | |
2986 | const QList<Atom *> &Doc::keywords() const |
2987 | { |
2988 | priv->constructExtra(); |
2989 | return priv->extra->keywords; |
2990 | } |
2991 | |
2992 | const QList<Atom *> &Doc::targets() const |
2993 | { |
2994 | priv->constructExtra(); |
2995 | return priv->extra->targets; |
2996 | } |
2997 | |
2998 | const QStringMultiMap &Doc::metaTagMap() const |
2999 | { |
3000 | return priv && priv->extra ? priv->extra->metaMap : *null_QStringMultiMap(); |
3001 | } |
3002 | |
3003 | void Doc::initialize(const Config& config) |
3004 | { |
3005 | DocParser::tabSize = config.getInt(CONFIG_TABSIZE); |
3006 | DocParser::exampleFiles = config.getStringList(CONFIG_EXAMPLES); |
3007 | DocParser::exampleDirs = config.getStringList(CONFIG_EXAMPLEDIRS); |
3008 | DocParser::sourceFiles = config.getStringList(CONFIG_SOURCES); |
3009 | DocParser::sourceDirs = config.getStringList(CONFIG_SOURCEDIRS); |
3010 | DocParser::quoting = config.getBool(CONFIG_QUOTINGINFORMATION); |
3011 | |
3012 | QmlClassNode::qmlOnly = config.getBool(CONFIG_QMLONLY); |
3013 | |
3014 | QStringMap reverseAliasMap; |
3015 | |
3016 | QSet<QString> commands = config.subVars(CONFIG_ALIAS); |
3017 | QSet<QString>::ConstIterator c = commands.begin(); |
3018 | while (c != commands.end()) { |
3019 | QString alias = config.getString(CONFIG_ALIAS + Config::dot + *c); |
3020 | if (reverseAliasMap.contains(alias)) { |
3021 | config.lastLocation().warning(tr("Command name '\\%1' cannot stand" |
3022 | " for both '\\%2' and '\\%3'") |
3023 | .arg(alias) |
3024 | .arg(reverseAliasMap[alias]) |
3025 | .arg(*c)); |
3026 | } |
3027 | else { |
3028 | reverseAliasMap.insert(alias, *c); |
3029 | } |
3030 | aliasMap()->insert(*c, alias); |
3031 | ++c; |
3032 | } |
3033 | |
3034 | int i = 0; |
3035 | while (cmds[i].english) { |
3036 | cmds[i].alias = new QString(alias(cmds[i].english)); |
3037 | cmdHash()->insert(*cmds[i].alias, cmds[i].no); |
3038 | |
3039 | if (cmds[i].no != i) |
3040 | Location::internalError(tr("command %1 missing").arg(i)); |
3041 | i++; |
3042 | } |
3043 | |
3044 | QSet<QString> macroNames = config.subVars(CONFIG_MACRO); |
3045 | QSet<QString>::ConstIterator n = macroNames.begin(); |
3046 | while (n != macroNames.end()) { |
3047 | QString macroDotName = CONFIG_MACRO + Config::dot + *n; |
3048 | Macro macro; |
3049 | macro.numParams = -1; |
3050 | macro.defaultDef = config.getString(macroDotName); |
3051 | if (!macro.defaultDef.isEmpty()) { |
3052 | macro.defaultDefLocation = config.lastLocation(); |
3053 | macro.numParams = Config::numParams(macro.defaultDef); |
3054 | } |
3055 | bool silent = false; |
3056 | |
3057 | QSet<QString> formats = config.subVars(macroDotName); |
3058 | QSet<QString>::ConstIterator f = formats.begin(); |
3059 | while (f != formats.end()) { |
3060 | QString def = config.getString(macroDotName + Config::dot + *f); |
3061 | if (!def.isEmpty()) { |
3062 | macro.otherDefs.insert(*f, def); |
3063 | int m = Config::numParams(def); |
3064 | if (macro.numParams == -1) { |
3065 | macro.numParams = m; |
3066 | } |
3067 | else if (macro.numParams != m) { |
3068 | if (!silent) { |
3069 | QString other = tr("default"); |
3070 | if (macro.defaultDef.isEmpty()) |
3071 | other = macro.otherDefs.begin().key(); |
3072 | config.lastLocation().warning(tr("Macro '\\%1' takes" |
3073 | " inconsistent number" |
3074 | " of arguments (%2" |
3075 | " %3, %4 %5)") |
3076 | .arg(*n) |
3077 | .arg(*f) |
3078 | .arg(m) |
3079 | .arg(other) |
3080 | .arg(macro.numParams)); |
3081 | silent = true; |
3082 | } |
3083 | if (macro.numParams < m) |
3084 | macro.numParams = m; |
3085 | } |
3086 | } |
3087 | ++f; |
3088 | } |
3089 | |
3090 | if (macro.numParams != -1) |
3091 | macroHash()->insert(*n, macro); |
3092 | ++n; |
3093 | } |
3094 | } |
3095 | |
3096 | void Doc::terminate() |
3097 | { |
3098 | DocParser::exampleFiles.clear(); |
3099 | DocParser::exampleDirs.clear(); |
3100 | DocParser::sourceFiles.clear(); |
3101 | DocParser::sourceDirs.clear(); |
3102 | aliasMap()->clear(); |
3103 | cmdHash()->clear(); |
3104 | macroHash()->clear(); |
3105 | |
3106 | int i = 0; |
3107 | while (cmds[i].english) { |
3108 | delete cmds[i].alias; |
3109 | cmds[i].alias = 0; |
3110 | ++i; |
3111 | } |
3112 | } |
3113 | |
3114 | QString Doc::alias(const QString &english) |
3115 | { |
3116 | return aliasMap()->value(english, english); |
3117 | } |
3118 | |
3119 | /*! |
3120 | Trims the deadwood out of \a str. i.e., this function |
3121 | cleans up \a str. |
3122 | */ |
3123 | void Doc::trimCStyleComment(Location& location, QString& str) |
3124 | { |
3125 | QString cleaned; |
3126 | Location m = location; |
3127 | bool metAsterColumn = true; |
3128 | int asterColumn = location.columnNo() + 1; |
3129 | int i; |
3130 | |
3131 | for (i = 0; i < (int) str.length(); i++) { |
3132 | if (m.columnNo() == asterColumn) { |
3133 | if (str[i] != |
3134 | break; |
3135 | cleaned += |
3136 | metAsterColumn = true; |
3137 | } |
3138 | else { |
3139 | if (str[i] == |
3140 | if (!metAsterColumn) |
3141 | break; |
3142 | metAsterColumn = false; |
3143 | } |
3144 | cleaned += str[i]; |
3145 | } |
3146 | m.advance(str[i]); |
3147 | } |
3148 | if (cleaned.length() == str.length()) |
3149 | str = cleaned; |
3150 | |
3151 | for (int i = 0; i < 3; i++) |
3152 | location.advance(str[i]); |
3153 | str = str.mid(3, str.length() - 5); |
3154 | } |
3155 | |
3156 | CodeMarker *Doc::quoteFromFile(const Location &location, |
3157 | Quoter "er, |
3158 | const QString &fileName) |
3159 | { |
3160 | quoter.reset(); |
3161 | |
3162 | QString code; |
3163 | |
3164 | QString userFriendlyFilePath; |
3165 | QString filePath = Config::findFile(location, |
3166 | DocParser::exampleFiles, |
3167 | DocParser::exampleDirs, |
3168 | fileName, userFriendlyFilePath); |
3169 | if (filePath.isEmpty()) { |
3170 | location.warning(tr("Cannot find example file '%1'").arg(fileName)); |
3171 | } |
3172 | else { |
3173 | QFile inFile(filePath); |
3174 | if (!inFile.open(QFile::ReadOnly)) { |
3175 | location.warning(tr("Cannot open example file '%1'").arg(userFriendlyFilePath)); |
3176 | } |
3177 | else { |
3178 | QTextStream inStream(&inFile); |
3179 | code = DocParser::untabifyEtc(inStream.readAll()); |
3180 | } |
3181 | } |
3182 | |
3183 | QString dirPath = QFileInfo(filePath).path(); |
3184 | CodeMarker *marker = CodeMarker::markerForFileName(fileName); |
3185 | quoter.quoteFromFile(userFriendlyFilePath, |
3186 | code, |
3187 | marker->markedUpCode(code, 0, location)); |
3188 | return marker; |
3189 | } |
3190 | |
3191 | QString Doc::canonicalTitle(const QString &title) |
3192 | { |
3193 | // The code below is equivalent to the following chunk, but _much_ |
3194 | // faster (accounts for ~10% of total running time) |
3195 | // |
3196 | // QRegExp attributeExpr("[^A-Za-z0-9]+"); |
3197 | // QString result = title.toLower(); |
3198 | // result.replace(attributeExpr, " "); |
3199 | // result = result.simplified(); |
3200 | // result.replace(QLatin1Char(' '), QLatin1Char('-')); |
3201 | |
3202 | QString result; |
3203 | result.reserve(title.size()); |
3204 | |
3205 | bool dashAppended = false; |
3206 | bool begun = false; |
3207 | int lastAlnum = 0; |
3208 | for (int i = 0; i != title.size(); ++i) { |
3209 | uint c = title.at(i).unicode(); |
3210 | if (c >= |
3211 | c -= |
3212 | bool alnum = (c >= |
3213 | if (alnum) { |
3214 | result += QLatin1Char(c); |
3215 | begun = true; |
3216 | dashAppended = false; |
3217 | lastAlnum = result.size(); |
3218 | } |
3219 | else if (!dashAppended) { |
3220 | if (begun) |
3221 | result += QLatin1Char( |
3222 | dashAppended = true; |
3223 | } |
3224 | } |
3225 | result.truncate(lastAlnum); |
3226 | return result; |
3227 | } |
3228 | |
3229 | void Doc::detach() |
3230 | { |
3231 | if (!priv) { |
3232 | priv = new DocPrivate; |
3233 | return; |
3234 | } |
3235 | if (priv->count == 1) |
3236 | return; |
3237 | |
3238 | --priv->count; |
3239 | |
3240 | DocPrivate *newPriv = new DocPrivate(*priv); |
3241 | newPriv->count = 1; |
3242 | if (priv->extra) |
3243 | newPriv->extra = new DocPrivateExtra(*priv->extra); |
3244 | |
3245 | priv = newPriv; |
3246 | } |
3247 | |
3248 | QT_END_NAMESPACE |
3249 |
Warning: That file was not part of the compilation database. It may have many parsing errors.