Skip to content

Commit 76f5a1e

Browse files
committed
Adding raise option for @include{doc}
1 parent 350f0f1 commit 76f5a1e

File tree

10 files changed

+239
-21
lines changed

10 files changed

+239
-21
lines changed

doc/commands.dox

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2745,9 +2745,11 @@ Commands for displaying examples
27452745
- The `option` `local` can be used make doxygen interpret the code as if it was in the
27462746
class or namespace in which the include command appears, rather than the global namespace.
27472747

2748-
\note When using the `{doc}` option,
2749-
some commands like \ref cmdcond "\\cond" and \ref cmdif "\\if" don't work with
2750-
this command due to the moment of parsing.
2748+
When using option `doc`, there is an second option `raise` that can be specified to raise
2749+
all sections found in the referenced file by a certain amount. For example `\\include{doc,raise=1} file.dox` will
2750+
treat any level 1 \c \\section found in `file.dox` as a level 2 \c \\subsection, and any level 2 \c \\subsection
2751+
into a level 3 \c \\subsubsection, etc.
2752+
Similarly, for Markdown a \c \# section will be treated as a \c \#\# section.
27512753

27522754
\note The included documentation should not have comment signs in it as they will appear
27532755
in the documentation as well.
@@ -2881,9 +2883,11 @@ Commands for displaying examples
28812883
- The `option` `local` can be used make doxygen interpret the code as if it was in the
28822884
class or namespace in which the include command appears, rather than the global namespace.
28832885

2884-
\note When using the `{doc}` option,
2885-
some commands like \ref cmdcond "\\cond" and \ref cmdif "\\if" don't work with
2886-
this command due to the moment of parsing.
2886+
When using option `doc`, there is an second option `raise` that can be specified to raise
2887+
all sections found in the referenced file by a certain amount. For example `\\snippet{doc,raise=1} file.dox XXX` will
2888+
treat any level 1 \c \\section found the snippet as a level 2 \c \\subsection, and any level 2 \c \\subsection
2889+
into a level 3 \c \\subsubsection, etc.
2890+
Similarly, for Markdown a \c \# section will be treated as a \c \#\# section.
28872891

28882892
\note The included documentation should not have comment signs in it as they will appear
28892893
in the documentation as well.

src/commentcnv.l

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ typedef yyguts_t *yyscan_t;
4343
#include "condparser.h"
4444
#include "fileinfo.h"
4545
#include "stringutil.h"
46+
#include "regex.h"
4647

4748
#include <assert.h>
4849

@@ -72,14 +73,15 @@ struct commentcnv_FileState
7273
QCString fileName;
7374
QCString oldFileName;
7475
QCString oldIncPrefix;
76+
QCString blockId;
7577
int oldState = 0;
7678
std::string fileBuf;
7779
const std::string *oldFileBuf = nullptr;
7880
int oldFileBufPos = 0;
7981
int oldIncludeCtx = 0;
82+
int oldRaiseLvl = 0;
8083
};
8184

82-
8385
struct commentcnvYY_state
8486
{
8587
commentcnvYY_state(const std::string *i,std::string &o) : inBuf(i), outBuf(o) {}
@@ -92,6 +94,8 @@ struct commentcnvYY_state
9294
bool mlBrief = FALSE;
9395
int readLineCtx = 0;
9496
int includeCtx = 0;
97+
int raiseLevel = 0;
98+
int raiseIncrement = 0;
9599
bool skip = FALSE;
96100
QCString fileName;
97101
int lineNr = 0;
@@ -139,6 +143,7 @@ static void replaceComment(yyscan_t yyscanner,int offset);
139143
static void clearCommentStack(yyscan_t yyscanner);
140144
static bool readIncludeFile(yyscan_t yyscanner,const QCString &inc,const QCString &blockId);
141145
static void insertCommentStart(yyscan_t yyscanner);
146+
static void parseIncludeOptions(yyscan_t yyscanner,std::string_view s);
142147

143148
#undef YY_INPUT
144149
#define YY_INPUT(buf,result,max_size) result=yyread(yyscanner,buf,max_size);
@@ -207,6 +212,8 @@ FILEMASK {VFILEMASK}|{HFILEMASK}
207212
B [ \t]
208213
ID [$a-z_A-Z\x80-\xFF][$a-z_A-Z0-9\x80-\xFF]*
209214
215+
OPTS (","{B}*{ID}{B}*("="{B}*({ID}|[0-9]*){B}*)?)*
216+
210217
//- end: NUMBER ---------------------------------------------------------------------------
211218
212219
// C start comment
@@ -1023,7 +1030,7 @@ SLASHopt [/]*
10231030
copyToOutput(yyscanner,yytext,yyleng);
10241031
}
10251032

1026-
<CComment,ReadLine,IncludeFile>[ \t][ \t][ \t][ \t][\\@]("include{"{B}*"doc"{B}*"}"|"includedoc") { // Markdown code section
1033+
<CComment,ReadLine,IncludeFile>{B}{B}{B}{B}[\\@]("include{"{B}*"doc"{B}*{OPTS}"}"|"includedoc") { // Markdown code section
10271034
if (Config_getBool(MARKDOWN_SUPPORT))
10281035
{
10291036
copyToOutput(yyscanner,yytext,yyleng);
@@ -1033,7 +1040,7 @@ SLASHopt [/]*
10331040
REJECT;
10341041
}
10351042
}
1036-
<CComment,ReadLine,IncludeFile>[ \t][ \t][ \t][ \t][\\@]("snippet{"{B}*"doc"{B}*"}"|"snippetdoc") { // Markdown code section
1043+
<CComment,ReadLine,IncludeFile>{B}{B}{B}{B}[\\@]("snippet{"{B}*"doc"{B}*{OPTS}"}"|"snippetdoc") { // Markdown code section
10371044
if (Config_getBool(MARKDOWN_SUPPORT))
10381045
{
10391046
copyToOutput(yyscanner,yytext,yyleng);
@@ -1043,9 +1050,10 @@ SLASHopt [/]*
10431050
REJECT;
10441051
}
10451052
}
1046-
<CComment,ReadLine,IncludeFile>[\\@]("include{"{B}*"doc"{B}*"}"|"includedoc") {
1053+
<CComment,ReadLine,IncludeFile>[\\@]("include{"{B}*"doc"{B}*{OPTS}"}"|"includedoc") {
10471054
yyextra->includeCtx = YY_START;
10481055
yyextra->firstIncludeLine = true;
1056+
parseIncludeOptions(yyscanner,std::string_view{yytext,static_cast<size_t>(yyleng)});
10491057
if (!yyextra->insertCppCommentMarker && (yyextra->includeCtx==ReadLine || yyextra->includeCtx==IncludeFile))
10501058
{
10511059
if (yyextra->includeCtx==ReadLine)
@@ -1056,9 +1064,10 @@ SLASHopt [/]*
10561064
}
10571065
BEGIN(IncludeDoc);
10581066
}
1059-
<CComment,ReadLine,IncludeFile>[\\@]("snippet{"{B}*"doc"{B}*"}"|"snippetdoc") {
1067+
<CComment,ReadLine,IncludeFile>[\\@]("snippet{"{B}*"doc"{B}*{OPTS}"}"|"snippetdoc") {
10601068
yyextra->includeCtx = YY_START;
10611069
yyextra->firstIncludeLine = true;
1070+
parseIncludeOptions(yyscanner,std::string_view{yytext,static_cast<size_t>(yyleng)});
10621071
if (!yyextra->insertCppCommentMarker && (yyextra->includeCtx==ReadLine || yyextra->includeCtx==IncludeFile))
10631072
{
10641073
if (yyextra->includeCtx==ReadLine)
@@ -1264,12 +1273,17 @@ SLASHopt [/]*
12641273
yy_switch_to_buffer(fs->bufState, yyscanner);
12651274
yy_delete_buffer(oldBuf, yyscanner);
12661275
BEGIN(fs->oldState);
1267-
yyextra->fileName = fs->oldFileName;
1268-
yyextra->lineNr = fs->oldLineNr;
1269-
yyextra->inBuf = fs->oldFileBuf;
1270-
yyextra->inBufPos = fs->oldFileBufPos;
1271-
yyextra->includeCtx = fs->oldIncludeCtx;
1276+
yyextra->fileName = fs->oldFileName;
1277+
yyextra->lineNr = fs->oldLineNr;
1278+
yyextra->inBuf = fs->oldFileBuf;
1279+
yyextra->inBufPos = fs->oldFileBufPos;
1280+
yyextra->includeCtx = fs->oldIncludeCtx;
12721281
QCString lineStr= " \\ilinebr \\ifile \""+yyextra->fileName+"\" \\iline "+QCString().setNum(yyextra->lineNr)+" ";
1282+
if (fs->oldRaiseLvl!=yyextra->raiseLevel)
1283+
{
1284+
lineStr+=std::string(" \\iraise ") + std::to_string(fs->oldRaiseLvl);
1285+
}
1286+
yyextra->raiseLevel = fs->oldRaiseLvl;
12731287
copyToOutput(yyscanner,lineStr.view());
12741288
yyextra->includeStack.pop_back();
12751289
//printf("<<EOF>> switch back to %s line %d inbufPos=%d outbufPos=%d\n",
@@ -1281,6 +1295,26 @@ SLASHopt [/]*
12811295
*/
12821296
%%
12831297

1298+
static void parseIncludeOptions(yyscan_t yyscanner,std::string_view s)
1299+
{
1300+
struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1301+
//printf("parseIncludeOptions=%s\n",qPrint(QCString(s)));
1302+
static const reg::Ex re(R"(,\s*raise\s*=\s*(\d+))");
1303+
reg::Match match;
1304+
if (reg::search(s,match,re))
1305+
{
1306+
const int maxLevel = 5;
1307+
yyextra->raiseIncrement = atoi(match[1].str().c_str());
1308+
if (yyextra->raiseLevel+yyextra->raiseIncrement>maxLevel) // check range
1309+
{
1310+
warn(yyextra->fileName,yyextra->lineNr,"Raising section level from %d to %d, exceeds allowed range [0-5], adjusting",
1311+
yyextra->raiseLevel,yyextra->raiseLevel+yyextra->raiseIncrement);
1312+
yyextra->raiseIncrement = std::max(0,maxLevel-yyextra->raiseLevel);
1313+
}
1314+
}
1315+
}
1316+
1317+
12841318
static void replaceCommentMarker(yyscan_t yyscanner,std::string_view s)
12851319
{
12861320
struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
@@ -1550,8 +1584,8 @@ static bool readIncludeFile(yyscan_t yyscanner,const QCString &inc,const QCStrin
15501584
bool alreadyProcessed = std::any_of(
15511585
yyextra->includeStack.begin(),
15521586
yyextra->includeStack.end(),
1553-
[absFileName](const auto &lfs)
1554-
{ return lfs->fileName==absFileName; }
1587+
[&absFileName,&blockId](const auto &lfs)
1588+
{ return lfs->fileName==absFileName && lfs->blockId==blockId; }
15551589
);
15561590

15571591
if (alreadyProcessed)
@@ -1595,7 +1629,14 @@ static bool readIncludeFile(yyscan_t yyscanner,const QCString &inc,const QCStrin
15951629
fs->fileBuf.append(incText.str());
15961630
}
15971631
}
1598-
QCString lineStr=" \\ilinebr \\ifile \""+absFileName+"\" \\iline " + QCString().setNum(lineNr) + " \\ilinebr ";
1632+
int oldRaiseLevel = yyextra->raiseLevel;
1633+
yyextra->raiseLevel+=yyextra->raiseIncrement;
1634+
QCString lineStr=" \\ilinebr \\ifile \""+absFileName+"\" \\iline " + std::to_string(lineNr);
1635+
if (yyextra->raiseLevel>0)
1636+
{
1637+
lineStr+=" \\iraise " + std::to_string(yyextra->raiseLevel);
1638+
}
1639+
lineStr+=" \\ilinebr ";
15991640
copyToOutput(yyscanner,lineStr.view());
16001641

16011642
fs->fileName = absFileName;
@@ -1606,6 +1647,8 @@ static bool readIncludeFile(yyscan_t yyscanner,const QCString &inc,const QCStrin
16061647
fs->oldFileBuf = yyextra->inBuf;
16071648
fs->oldFileBufPos = yyextra->inBufPos;
16081649
fs->oldIncludeCtx = yyextra->includeCtx;
1650+
fs->oldRaiseLvl = oldRaiseLevel;
1651+
fs->blockId = blockId;
16091652
yy_switch_to_buffer(yy_create_buffer(0, YY_BUF_SIZE, yyscanner),yyscanner);
16101653
yyextra->fileName = absFileName;
16111654
yyextra->lineNr = lineNr;
@@ -1756,6 +1799,8 @@ void convertCppComments(const std::string &inBuf,std::string &outBuf,const std::
17561799
yyextra->lang = getLanguageFromFileName(fileName);
17571800
yyextra->pythonDocString = FALSE;
17581801
yyextra->lineNr = 1;
1802+
yyextra->raiseLevel = 0;
1803+
yyextra->raiseIncrement = 0;
17591804
yyextra->insertCppCommentMarker=false;
17601805
yyextra->expandedAliases.clear();
17611806
while (!yyextra->condStack.empty()) yyextra->condStack.pop();

src/commentscan.l

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ typedef yyguts_t *yyscan_t;
3737
#include <mutex>
3838
#include <functional>
3939
#include <unordered_map>
40+
#include <algorithm>
4041

4142
#include <stdio.h>
4243
#include <stdlib.h>
@@ -168,6 +169,7 @@ static bool handleLineInfo(yyscan_t yyscanner,const QCString &, const StringVect
168169
static bool handleModule(yyscan_t yyscanner,const QCString &, const StringVector &);
169170
static bool handleIFile(yyscan_t yyscanner,const QCString &, const StringVector &);
170171
static bool handleILine(yyscan_t yyscanner,const QCString &, const StringVector &);
172+
static bool handleIRaise(yyscan_t yyscanner,const QCString &, const StringVector &);
171173

172174
[[maybe_unused]] static const char *stateToString(int state);
173175

@@ -344,6 +346,7 @@ static const std::map< std::string, DocCmdMap > docCmdMap =
344346
{ "static", { &handleStatic, CommandSpacing::Invisible, SectionHandling::Escape }},
345347
{ "struct", { &handleStruct, CommandSpacing::Invisible, SectionHandling::Escape }},
346348
{ "subpage", { &handleSubpage, CommandSpacing::Inline, SectionHandling::Allowed }},
349+
{ "subparagraph", { &handleSection, CommandSpacing::Block, SectionHandling::Break }},
347350
{ "subsection", { &handleSection, CommandSpacing::Block, SectionHandling::Break }},
348351
{ "subsubsection", { &handleSection, CommandSpacing::Block, SectionHandling::Break }},
349352
{ "tableofcontents", { &handleToc, CommandSpacing::Invisible, SectionHandling::Break }},
@@ -391,7 +394,8 @@ static const std::map< std::string, DocCmdMap > docCmdMap =
391394
{ "link", { 0, CommandSpacing::Invisible, SectionHandling::Replace }},
392395
{ "endlink", { 0, CommandSpacing::Invisible, SectionHandling::Escape }},
393396
{ "ifile", { &handleIFile, CommandSpacing::Invisible, SectionHandling::Replace }},
394-
{ "iline", { &handleILine, CommandSpacing::Invisible, SectionHandling::Replace }}
397+
{ "iline", { &handleILine, CommandSpacing::Invisible, SectionHandling::Replace }},
398+
{ "iraise", { &handleIRaise, CommandSpacing::Invisible, SectionHandling::Replace }}
395399
};
396400

397401
#define YY_NO_INPUT 1
@@ -458,6 +462,7 @@ struct commentscanYY_state
458462
int inputPosition = 0; // read pointer
459463
QCString fileName; // file name that is read from
460464
int lineNr = 0; // line number in the input
465+
int raiseLevel = 0; // section level raise amount
461466
bool inBody = FALSE; // was the comment found inside the body of a function?
462467
OutputContext inContext; // are we inside the brief, details or xref part
463468
bool briefEndsAtDot = FALSE; // does the brief description stop at a dot?
@@ -686,6 +691,7 @@ STopt [^\n@\\]*
686691
%x IFileSection
687692
%x ILine
688693
%x ILineSection
694+
%x IRaise
689695

690696
%%
691697

@@ -1741,6 +1747,29 @@ STopt [^\n@\\]*
17411747
}
17421748
}
17431749

1750+
/* ----- handle arguments of the iraise command ----- */
1751+
<IRaise>{B}*[0-9]+/[\\@\n\.] |
1752+
<IRaise>{B}*[0-9]+{B} {
1753+
bool ok = false;
1754+
int nr = QCString(yytext).toInt(&ok);
1755+
if (!ok)
1756+
{
1757+
warn(yyextra->fileName,yyextra->lineNr,"Invalid level '%s' for iraise command",yytext);
1758+
}
1759+
else
1760+
{
1761+
yyextra->raiseLevel = nr;
1762+
}
1763+
BEGIN(Comment);
1764+
}
1765+
<IRaise>. {
1766+
unput(yytext[0]);
1767+
BEGIN(Comment);
1768+
}
1769+
1770+
1771+
/* ----- handle arguments of the ifile command ----- */
1772+
17441773
<IFile,IFileSection>{FILE} {
17451774
addOutput(yyscanner,yytext);
17461775
QCString text(yytext);
@@ -3148,12 +3177,26 @@ static bool handleSection(yyscan_t yyscanner,const QCString &s, const StringVect
31483177
{
31493178
struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
31503179
setOutput(yyscanner,OutputDoc);
3151-
addOutput(yyscanner,"@"+s+" ");
3180+
//printf("handleSection(%s) raiseLevel=%d\n",qPrint(s),yyextra->raiseLevel);
31523181
BEGIN(SectionLabel);
3182+
// determine natural section level
31533183
if (s=="section") yyextra->sectionLevel=1;
31543184
else if (s=="subsection") yyextra->sectionLevel=2;
31553185
else if (s=="subsubsection") yyextra->sectionLevel=3;
31563186
else if (s=="paragraph") yyextra->sectionLevel=4;
3187+
else if (s=="subparagraph") yyextra->sectionLevel=5;
3188+
// raise it if requested
3189+
yyextra->sectionLevel = std::min(yyextra->sectionLevel + yyextra->raiseLevel,5);
3190+
// rewrite the update section level to the output
3191+
switch (yyextra->sectionLevel)
3192+
{
3193+
case 1: addOutput(yyscanner,"@section "); break;
3194+
case 2: addOutput(yyscanner,"@subsection "); break;
3195+
case 3: addOutput(yyscanner,"@subsubsection "); break;
3196+
case 4: addOutput(yyscanner,"@paragraph "); break;
3197+
case 5: addOutput(yyscanner,"@subparagraph "); break;
3198+
default: addOutput(yyscanner,"@"+s+" "); break;
3199+
}
31573200
return FALSE;
31583201
}
31593202

@@ -3388,6 +3431,14 @@ static bool handleIFile(yyscan_t yyscanner,const QCString &, const StringVector
33883431
return FALSE;
33893432
}
33903433

3434+
static bool handleIRaise(yyscan_t yyscanner,const QCString &, const StringVector &)
3435+
{
3436+
struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
3437+
BEGIN(IRaise);
3438+
return FALSE;
3439+
}
3440+
3441+
33913442
static bool handleIf(yyscan_t yyscanner,const QCString &, const StringVector &)
33923443
{
33933444
struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;

testing/062/md_062__main.xml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+
<doxygen xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="compound.xsd" version="" xml:lang="en-US">
3+
<compounddef id="md_062__main" kind="page">
4+
<compoundname>md_062__main</compoundname>
5+
<title>062_main</title>
6+
<briefdescription>
7+
</briefdescription>
8+
<detaileddescription>
9+
<para>Some text in main.md</para>
10+
<sect1 id="md_062__main_1autotoc_md0">
11+
<title>Section 1</title>
12+
<sect2 id="md_062__main_1autotoc_md1">
13+
<title>Included Section 1.1</title>
14+
<para>Some text in sub.md.</para>
15+
<sect3 id="md_062__main_1autotoc_md2">
16+
<title>Included Section x.1.1</title>
17+
<para>Some text in subsub.md.</para>
18+
</sect3>
19+
</sect2>
20+
</sect1>
21+
<sect1 id="md_062__main_1autotoc_md3">
22+
<title>Section 2</title>
23+
<sect2 id="md_062__main_1autotoc_md4">
24+
<title>Subsection 2.1</title>
25+
<sect3 id="md_062__main_1autotoc_md5">
26+
<title>Included Section x.1.1</title>
27+
<para>Some text in subsub.md.</para>
28+
</sect3>
29+
</sect2>
30+
</sect1>
31+
</detaileddescription>
32+
<location file="062_main.md"/>
33+
</compounddef>
34+
</doxygen>

testing/062_main.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!--
2+
// objective: test @include{doc} command with raise option
3+
// check: md_062__main.xml
4+
-->
5+
[TOC]
6+
7+
Some text in main.md
8+
9+
# Section 1
10+
@include{doc,raise=1} sub.md
11+
12+
# Section 2
13+
## Subsection 2.1
14+
@include{doc,raise=2} subsub.md
15+

0 commit comments

Comments
 (0)