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