Skip to content

Commit d2f2a5f

Browse files
committed
issued #7589 Add Markdown todo list
Adding check boxes available by means of indicators `- [ ]` / `- [x]` / `- [X]`. Adding checkbox possibilities to the different output formats Updating documentation Small example: [example.tar.gz](https://github.com/doxygen/doxygen/files/4226518/example.tar.gz) This actually the same implementation as with the original proposed pull request #7602 though due to changes in time it was not easy (possible) to merge the original against master
1 parent cd5d623 commit d2f2a5f

22 files changed

Lines changed: 372 additions & 23 deletions

addon/doxmlparser/doxmlparser/compound.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,11 @@ class DoxBool(str, Enum):
989989
NO='no'
990990

991991

992+
class DoxCheck(str, Enum):
993+
CHECKED='checked'
994+
UNCHECKED='unchecked'
995+
996+
992997
class DoxCompoundKind(str, Enum):
993998
CLASS='class'
994999
STRUCT='struct'
@@ -17381,12 +17386,14 @@ class docListItemType(GeneratedsSuper):
1738117386
__hash__ = GeneratedsSuper.__hash__
1738217387
subclass = None
1738317388
superclass = None
17384-
def __init__(self, value=None, para=None, gds_collector_=None, **kwargs_):
17389+
def __init__(self, override=None, value=None, para=None, gds_collector_=None, **kwargs_):
1738517390
self.gds_collector_ = gds_collector_
1738617391
self.gds_elementtree_node_ = None
1738717392
self.original_tagname_ = None
1738817393
self.parent_object_ = kwargs_.get('parent_object_')
1738917394
self.ns_prefix_ = None
17395+
self.override = _cast(None, override)
17396+
self.override_nsprefix_ = None
1739017397
self.value = _cast(int, value)
1739117398
self.value_nsprefix_ = None
1739217399
if para is None:
@@ -17419,10 +17426,27 @@ def insert_para_at(self, index, value):
1741917426
self.para.insert(index, value)
1742017427
def replace_para_at(self, index, value):
1742117428
self.para[index] = value
17429+
def get_override(self):
17430+
return self.override
17431+
def set_override(self, override):
17432+
self.override = override
1742217433
def get_value(self):
1742317434
return self.value
1742417435
def set_value(self, value):
1742517436
self.value = value
17437+
def validate_DoxCheck(self, value):
17438+
# Validate type DoxCheck, a restriction on xsd:string.
17439+
if value is not None and Validate_simpletypes_ and self.gds_collector_ is not None:
17440+
if not isinstance(value, str):
17441+
lineno = self.gds_get_node_lineno_()
17442+
self.gds_collector_.add_message('Value "%(value)s"%(lineno)s is not of the correct base simple type (str)' % {"value": value, "lineno": lineno, })
17443+
return False
17444+
value = value
17445+
enumerations = ['checked', 'unchecked']
17446+
if value not in enumerations:
17447+
lineno = self.gds_get_node_lineno_()
17448+
self.gds_collector_.add_message('Value "%(value)s"%(lineno)s does not match xsd enumeration restriction on DoxCheck' % {"value" : encode_str_2_3(value), "lineno": lineno} )
17449+
result = False
1742617450
def hasContent_(self):
1742717451
if (
1742817452
self.para
@@ -17454,6 +17478,9 @@ def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='d
1745417478
else:
1745517479
outfile.write('/>%s' % (eol_, ))
1745617480
def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='docListItemType'):
17481+
if self.override is not None and 'override' not in already_processed:
17482+
already_processed.add('override')
17483+
outfile.write(' override=%s' % (self.gds_encode(self.gds_format_string(quote_attrib(self.override), input_name='override')), ))
1745717484
if self.value is not None and 'value' not in already_processed:
1745817485
already_processed.add('value')
1745917486
outfile.write(' value="%s"' % self.gds_format_integer(self.value, input_name='value'))
@@ -17477,6 +17504,11 @@ def build(self, node, gds_collector_=None):
1747717504
self.buildChildren(child, node, nodeName_, gds_collector_=gds_collector_)
1747817505
return self
1747917506
def buildAttributes(self, node, attrs, already_processed):
17507+
value = find_attr_value_('override', node)
17508+
if value is not None and 'override' not in already_processed:
17509+
already_processed.add('override')
17510+
self.override = value
17511+
self.validate_DoxCheck(self.override) # validate type DoxCheck
1748017512
value = find_attr_value_('value', node)
1748117513
if value is not None and 'value' not in already_processed:
1748217514
already_processed.add('value')

doc/lists.dox

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ sign also plus (`+`) or asterisk (`*`) can be used.
1111
Numbered lists can also be generated by using a minus followed by a hash (`#`)
1212
or by using a number followed by a dot.
1313

14+
Lists with as indicator a checked or unchecked check box are possible when having
15+
a minus followed by optional spaces and followed by `[ ]` for an unchecked
16+
check box and `[x]` or `[X]` for a checked check box.
17+
1418
Nesting of lists is allowed and is based on indentation of the items.<p>
1519
Here is an example:
1620

@@ -25,6 +29,9 @@ Here is an example:
2529
* - keyboard events
2630
* 1. key down event
2731
* 2. key up event
32+
* - checbox list
33+
* - [ ] unchecked
34+
* - [x] checked
2835
*
2936
* More text here.
3037
*/
@@ -40,6 +47,9 @@ Here is an example:
4047
- keyboard events
4148
1. key down event
4249
2. key up event
50+
- checbox list
51+
- [ ] unchecked
52+
- [x] checked
4353

4454
More text here.
4555

doc/markdown.dox

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,12 @@ lists by using `-#` markers:
724724

725725
-# item1
726726
-# item2
727+
Lists with as indicator a checked or unchecked check box are by using
728+
`- [ ]` or `- [x]` or `- [X]` as markers:
729+
730+
- [ ] unchecked
731+
- [x] checked
732+
727733

728734
\subsection mddox_stars Use of asterisks
729735

src/docbookvisitor.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,19 @@ void DocbookDocVisitor::operator()(const DocAutoListItem &li)
619619
{
620620
DB_VIS_C
621621
if (m_hide) return;
622-
m_t << "<listitem>";
622+
switch (li.itemNumber())
623+
{
624+
case DocAutoList::Unchecked: // unchecked
625+
m_t << "<listitem override=\"unchecked\">";
626+
break;
627+
case DocAutoList::Checked_x: // checked with x
628+
case DocAutoList::Checked_X: // checked with X
629+
m_t << "<listitem override=\"checked\">";
630+
break;
631+
default:
632+
m_t << "<listitem>";
633+
break;
634+
}
623635
visitChildren(li);
624636
m_t << "</listitem>";
625637
}

src/docnode.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2714,9 +2714,9 @@ int DocAutoListItem::parse()
27142714
//--------------------------------------------------------------------------
27152715

27162716
DocAutoList::DocAutoList(DocParser *parser,DocNodeVariant *parent,int indent,bool isEnumList,
2717-
int depth) :
2717+
int depth, bool isCheckedList):
27182718
DocCompoundNode(parser,parent), m_indent(indent), m_isEnumList(isEnumList),
2719-
m_depth(depth)
2719+
m_depth(depth),m_isCheckedList(isCheckedList)
27202720
{
27212721
}
27222722

@@ -2730,10 +2730,20 @@ int DocAutoList::parse()
27302730
// first item or sub list => create new list
27312731
do
27322732
{
2733-
if (parser()->context.token->id!=-1) // explicitly numbered list
2733+
switch (parser()->context.token->id)
27342734
{
2735-
num=parser()->context.token->id; // override num with real number given
2735+
case -1:
2736+
break;
2737+
case DocAutoList::Unchecked: // unchecked
2738+
case DocAutoList::Checked_x: // checked with x
2739+
case DocAutoList::Checked_X: // checked with X
2740+
num = parser()->context.token->id;
2741+
break;
2742+
default: // explicitly numbered list
2743+
num=parser()->context.token->id; // override num with real number given
2744+
break;
27362745
}
2746+
27372747
children().append<DocAutoListItem>(parser(),thisVariant(),m_indent,num++);
27382748
retval = children().get_last<DocAutoListItem>()->parse();
27392749
//printf("DocAutoList::parse(): retval=0x%x parser()->context.token->indent=%d m_indent=%d "
@@ -2745,7 +2755,8 @@ int DocAutoList::parse()
27452755
while (retval==TK_LISTITEM && // new list item
27462756
m_indent==parser()->context.token->indent && // at same indent level
27472757
m_isEnumList==parser()->context.token->isEnumList && // of the same kind
2748-
(parser()->context.token->id==-1 || parser()->context.token->id>=num) // increasing number (or no number)
2758+
m_isCheckedList==parser()->context.token->isCheckedList && // of the same kind
2759+
(parser()->context.token->id==-1 || parser()->context.token->id>=num) // increasing number (or no number or checked list)
27492760
);
27502761

27512762
parser()->tokenizer.endAutoList();
@@ -5344,7 +5355,8 @@ int DocPara::parse()
53445355
{
53455356
children().append<DocAutoList>(parser(),thisVariant(),
53465357
parser()->context.token->indent,
5347-
parser()->context.token->isEnumList,depth);
5358+
parser()->context.token->isEnumList,depth,
5359+
parser()->context.token->isCheckedList);
53485360
al = children().get_last<DocAutoList>();
53495361
retval = children().get_last<DocAutoList>()->parse();
53505362
} while (retval==TK_LISTITEM && // new list

src/docnode.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,15 +546,23 @@ class DocIndexEntry : public DocNode
546546
class DocAutoList : public DocCompoundNode
547547
{
548548
public:
549-
DocAutoList(DocParser *parser,DocNodeVariant *parent,int indent,bool isEnumList,int depth);
549+
enum ListType
550+
{
551+
Unnumbered=1, Unchecked=-2, Checked_x=-3, Checked_X=-4 // positive numbers give the label
552+
};
553+
DocAutoList(DocParser *parser,DocNodeVariant *parent,int indent,bool isEnumList,
554+
int depth, bool isCheckedList);
555+
550556
bool isEnumList() const { return m_isEnumList; }
551557
int indent() const { return m_indent; }
558+
bool isCheckedList() const { return m_isCheckedList; }
552559
int depth() const { return m_depth; }
553560
int parse();
554561

555562
private:
556563
int m_indent = 0;
557564
bool m_isEnumList = false;
565+
bool m_isCheckedList = false;
558566
int m_depth = 0;
559567
};
560568

src/doctokenizer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ struct TokenInfo
8383

8484
// list token info
8585
bool isEnumList = false;
86+
bool isCheckedList = false;
8687
int indent = 0;
8788

8889
// sections

src/doctokenizer.l

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ typedef yyguts_t *yyscan_t;
4747
#include "cite.h"
4848
#include "regex.h"
4949
#include "debug.h"
50+
#include "docnode.h"
5051

5152
#define YY_NO_INPUT 1
5253
#define YY_NO_UNISTD_H 1
@@ -272,6 +273,7 @@ OPTSTARS ("/""/"{BLANK}*)?"*"*{BLANK}*
272273
LISTITEM {BLANK}*[-]("#")?{WS}
273274
MLISTITEM {BLANK}*[+*]{WS}
274275
OLISTITEM {BLANK}*[1-9][0-9]*"."{BLANK}
276+
CLISTITEM {BLANK}*[-]{BLANK}*"\["[ xX]"\]"
275277
ENDLIST {BLANK}*"."{BLANK}*(\n|"\\ilinebr")
276278
ATTRNAME [a-z_A-Z\x80-\xFF][:a-z_A-Z0-9\x80-\xFF\-]*
277279
ATTRIB {ATTRNAME}{WS}*("="{WS}*(("\""[^\"]*"\"")|("'"[^\']*"'")|[^ \t\r\n'"><]+))?
@@ -414,10 +416,22 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
414416
uint32_t dashPos = static_cast<uint32_t>(text.findRev('-'));
415417
assert(dashPos!=static_cast<uint32_t>(-1));
416418
yyextra->token.isEnumList = text.at(dashPos+1)=='#';
419+
yyextra->token.isCheckedList = false;
417420
yyextra->token.id = -1;
418421
yyextra->token.indent = computeIndent(yytext,dashPos);
419422
return TK_LISTITEM;
420423
}
424+
<St_Para>^{CLISTITEM} { /* checkbox item */
425+
QCString text=yytext;
426+
int dashPos = text.findRev('-');
427+
yyextra->token.isEnumList = false;
428+
yyextra->token.isCheckedList = true;
429+
if (text.find('x') != -1) yyextra->token.id = DocAutoList::Checked_x;
430+
else if (text.find('X') != -1) yyextra->token.id = DocAutoList::Checked_X;
431+
else yyextra->token.id = DocAutoList::Unchecked;
432+
yyextra->token.indent = computeIndent(yytext,dashPos);
433+
return TK_LISTITEM;
434+
}
421435
<St_Para>^{MLISTITEM} { /* list item */
422436
if (yyextra->insideHtmlLink || !yyextra->markdownSupport || yyextra->insidePre)
423437
{
@@ -432,7 +446,8 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
432446
reg::search(text,match,re);
433447
size_t listPos = match.position();
434448
assert(listPos!=std::string::npos);
435-
yyextra->token.isEnumList = FALSE;
449+
yyextra->token.isEnumList = false;
450+
yyextra->token.isCheckedList = false;
436451
yyextra->token.id = -1;
437452
yyextra->token.indent = computeIndent(yytext,listPos);
438453
return TK_LISTITEM;
@@ -452,6 +467,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
452467
size_t markPos = match.position();
453468
assert(markPos!=std::string::npos);
454469
yyextra->token.isEnumList = true;
470+
yyextra->token.isCheckedList = false;
455471
bool ok = false;
456472
int id = QCString(match.str()).toInt(&ok);
457473
yyextra->token.id = ok ? id : -1;
@@ -470,10 +486,23 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
470486
uint32_t dashPos = static_cast<uint32_t>(text.findRev('-'));
471487
assert(dashPos!=static_cast<uint32_t>(-1));
472488
yyextra->token.isEnumList = text.at(dashPos+1)=='#';
489+
yyextra->token.isCheckedList = false;
473490
yyextra->token.id = -1;
474491
yyextra->token.indent = computeIndent(text.data(),dashPos);
475492
return TK_LISTITEM;
476493
}
494+
<St_Para>{BLANK}*\n{CLISTITEM} { /* checkbox item on next line */
495+
QCString text=yytext;
496+
text=text.right(text.length()-text.find('\n')-1);
497+
int dashPos = text.findRev('-');
498+
yyextra->token.isEnumList = false;
499+
yyextra->token.isCheckedList = true;
500+
if (text.find('x') != -1) yyextra->token.id = DocAutoList::Checked_x;
501+
else if (text.find('X') != -1) yyextra->token.id = DocAutoList::Checked_X;
502+
else yyextra->token.id = DocAutoList::Unchecked;
503+
yyextra->token.indent = computeIndent(text.data(),dashPos);
504+
return TK_LISTITEM;
505+
}
477506
<St_Para>{BLANK}*(\n|"\\ilinebr"){MLISTITEM} { /* list item on next line */
478507
if (yyextra->insideHtmlLink || !yyextra->markdownSupport || yyextra->insidePre)
479508
{
@@ -489,6 +518,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
489518
size_t markPos = match.position();
490519
assert(markPos!=std::string::npos);
491520
yyextra->token.isEnumList = FALSE;
521+
yyextra->token.isCheckedList = false;
492522
yyextra->token.id = -1;
493523
yyextra->token.indent = computeIndent(text.c_str(),markPos);
494524
return TK_LISTITEM;
@@ -509,6 +539,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
509539
size_t markPos = match.position();
510540
assert(markPos!=std::string::npos);
511541
yyextra->token.isEnumList = true;
542+
yyextra->token.isCheckedList = false;
512543
bool ok = false;
513544
int id = QCString(match.str()).toInt(&ok);
514545
yyextra->token.id = ok ? id : -1;
@@ -738,6 +769,12 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
738769
}
739770
lineCount(yytext,yyleng);
740771
}
772+
<St_Para>({BLANK}*\n)+{BLANK}*\n/{CLISTITEM} { /* skip trailing paragraph followed by new checkbox item */
773+
if (yyextra->insidePre || yyextra->autoListLevel==0)
774+
{
775+
REJECT;
776+
}
777+
}
741778
<St_Para>({BLANK}*\n)+{BLANK}*\n/{MLISTITEM} { /* skip trailing paragraph followed by new list item */
742779
if (!yyextra->markdownSupport || yyextra->insidePre || yyextra->autoListLevel==0)
743780
{

src/htmldocvisitor.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,7 +1028,14 @@ void HtmlDocVisitor::operator()(const DocAutoList &l)
10281028
}
10291029
else
10301030
{
1031-
m_t << "<ul>";
1031+
if (l.isCheckedList())
1032+
{
1033+
m_t << "<ul class=\"check\">";
1034+
}
1035+
else
1036+
{
1037+
m_t << "<ul>";
1038+
}
10321039
}
10331040
if (!l.isPreformatted()) m_t << "\n";
10341041
visitChildren(l);
@@ -1047,7 +1054,19 @@ void HtmlDocVisitor::operator()(const DocAutoList &l)
10471054
void HtmlDocVisitor::operator()(const DocAutoListItem &li)
10481055
{
10491056
if (m_hide) return;
1050-
m_t << "<li>";
1057+
switch (li.itemNumber())
1058+
{
1059+
case DocAutoList::Unchecked: // unchecked
1060+
m_t << "<li class=\"unchecked\">";
1061+
break;
1062+
case DocAutoList::Checked_x: // checked with x
1063+
case DocAutoList::Checked_X: // checked with X
1064+
m_t << "<li class=\"checked\">";
1065+
break;
1066+
default:
1067+
m_t << "<li>";
1068+
break;
1069+
}
10511070
visitChildren(li);
10521071
m_t << "</li>";
10531072
if (!li.isPreformatted()) m_t << "\n";

src/latexdocvisitor.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -754,7 +754,19 @@ void LatexDocVisitor::operator()(const DocAutoList &l)
754754
void LatexDocVisitor::operator()(const DocAutoListItem &li)
755755
{
756756
if (m_hide) return;
757-
m_t << "\n\\item ";
757+
switch (li.itemNumber())
758+
{
759+
case DocAutoList::Unchecked: // unchecked
760+
m_t << "\n\\item[\\DoxyUnchecked] ";
761+
break;
762+
case DocAutoList::Checked_x: // checked with x
763+
case DocAutoList::Checked_X: // checked with X
764+
m_t << "\n\\item[\\DoxyChecked] ";
765+
break;
766+
default:
767+
m_t << "\n\\item ";
768+
break;
769+
}
758770
incIndentLevel();
759771
visitChildren(li);
760772
decIndentLevel();

0 commit comments

Comments
 (0)