Fossil SCM

All submenu buttons on embedded documentation pages using markup of the form: <a class="button" href="...">LABEL</a>.

drh 2012-10-19 18:33 UTC trunk
Commit 563b3ccb7b676112740dfe89b87510ac1cac0f28
2 files changed +2 -2 +78 -17
+2 -2
--- src/doc.c
+++ src/doc.c
@@ -489,14 +489,14 @@
489489
" WHERE objid=%d AND type='ci'", vid));
490490
if( fossil_strcmp(zMime, "application/x-fossil-wiki")==0 ){
491491
Blob title, tail;
492492
if( wiki_find_title(&filebody, &title, &tail) ){
493493
style_header(blob_str(&title));
494
- wiki_convert(&tail, 0, 0);
494
+ wiki_convert(&tail, 0, WIKI_BUTTONS);
495495
}else{
496496
style_header("Documentation");
497
- wiki_convert(&filebody, 0, 0);
497
+ wiki_convert(&filebody, 0, WIKI_BUTTONS);
498498
}
499499
style_footer();
500500
}else if( fossil_strcmp(zMime, "text/plain")==0 ){
501501
style_header("Documentation");
502502
@ <blockquote><pre>
503503
--- src/doc.c
+++ src/doc.c
@@ -489,14 +489,14 @@
489 " WHERE objid=%d AND type='ci'", vid));
490 if( fossil_strcmp(zMime, "application/x-fossil-wiki")==0 ){
491 Blob title, tail;
492 if( wiki_find_title(&filebody, &title, &tail) ){
493 style_header(blob_str(&title));
494 wiki_convert(&tail, 0, 0);
495 }else{
496 style_header("Documentation");
497 wiki_convert(&filebody, 0, 0);
498 }
499 style_footer();
500 }else if( fossil_strcmp(zMime, "text/plain")==0 ){
501 style_header("Documentation");
502 @ <blockquote><pre>
503
--- src/doc.c
+++ src/doc.c
@@ -489,14 +489,14 @@
489 " WHERE objid=%d AND type='ci'", vid));
490 if( fossil_strcmp(zMime, "application/x-fossil-wiki")==0 ){
491 Blob title, tail;
492 if( wiki_find_title(&filebody, &title, &tail) ){
493 style_header(blob_str(&title));
494 wiki_convert(&tail, 0, WIKI_BUTTONS);
495 }else{
496 style_header("Documentation");
497 wiki_convert(&filebody, 0, WIKI_BUTTONS);
498 }
499 style_footer();
500 }else if( fossil_strcmp(zMime, "text/plain")==0 ){
501 style_header("Documentation");
502 @ <blockquote><pre>
503
+78 -17
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -27,10 +27,11 @@
2727
*/
2828
#define WIKI_NOFOLLOW 0x001
2929
#define WIKI_HTML 0x002
3030
#define WIKI_INLINE 0x004 /* Do not surround with <p>..</p> */
3131
#define WIKI_NOBLOCK 0x008 /* No block markup of any kind */
32
+#define WIKI_BUTTONS 0x010 /* Allow sub-menu buttons */
3233
#endif
3334
3435
3536
/*
3637
** These are the only markup attributes allowed.
@@ -378,19 +379,19 @@
378379
#define TOKEN_INDENT 9 /* " " */
379380
#define TOKEN_RAW 10 /* Output exactly (used when wiki-use-html==1) */
380381
#define TOKEN_TEXT 11 /* None of the above */
381382
382383
/*
383
-** State flags
384
+** State flags. Save the lower 16 bits for the WIKI_* flags.
384385
*/
385
-#define AT_NEWLINE 0x001 /* At start of a line */
386
-#define AT_PARAGRAPH 0x002 /* At start of a paragraph */
387
-#define ALLOW_WIKI 0x004 /* Allow wiki markup */
388
-#define FONT_MARKUP_ONLY 0x008 /* Only allow MUTYPE_FONT markup */
389
-#define INLINE_MARKUP_ONLY 0x010 /* Allow only "inline" markup */
390
-#define IN_LIST 0x020 /* Within wiki <ul> or <ol> */
391
-#define WIKI_USE_HTML 0x040 /* wiki-use-html option = on */
386
+#define AT_NEWLINE 0x0010000 /* At start of a line */
387
+#define AT_PARAGRAPH 0x0020000 /* At start of a paragraph */
388
+#define ALLOW_WIKI 0x0040000 /* Allow wiki markup */
389
+#define FONT_MARKUP_ONLY 0x0080000 /* Only allow MUTYPE_FONT markup */
390
+#define INLINE_MARKUP_ONLY 0x0100000 /* Allow only "inline" markup */
391
+#define IN_LIST 0x0200000 /* Within wiki <ul> or <ol> */
392
+#define WIKI_USE_HTML 0x0400000 /* wiki-use-html option = on */
392393
393394
/*
394395
** Current state of the rendering engine
395396
*/
396397
typedef struct Renderer Renderer;
@@ -825,22 +826,72 @@
825826
z[n] = p->aAttr[i].cTerm;
826827
}
827828
}
828829
829830
/*
830
-** Return the ID attribute for markup. Return NULL if there is no
831
+** Return the value of attribute attrId. Return NULL if there is no
831832
** ID attribute.
832833
*/
833
-static const char *markupId(ParsedMarkup *p){
834
+static const char *attributeValue(ParsedMarkup *p, int attrId){
834835
int i;
835836
for(i=0; i<p->nAttr; i++){
836
- if( p->aAttr[i].iACode==ATTR_ID ){
837
+ if( p->aAttr[i].iACode==attrId ){
837838
return p->aAttr[i].zValue;
838839
}
839840
}
840841
return 0;
841842
}
843
+
844
+/*
845
+** Return the ID attribute for markup. Return NULL if there is no
846
+** ID attribute.
847
+*/
848
+static const char *markupId(ParsedMarkup *p){
849
+ return attributeValue(p, ATTR_ID);
850
+}
851
+
852
+/*
853
+** Check markup pMarkup to see if it is a hyperlink with class "button"
854
+** that is follows by simple text and an </a> only. Example:
855
+**
856
+** <a class="button" href="../index.wiki">Index</a>
857
+**
858
+** If the markup matches this pattern, and if the WIKI_BUTTONS flag was
859
+** passed to wiki_convert(), then transform this link into a submenu
860
+** button, skip the text, and set *pN equal to the total length of the
861
+** text through the end of </a> and return true. If the markup does
862
+** not match or if WIKI_BUTTONS is not set, then make no changes to *pN
863
+** and return false.
864
+*/
865
+static int isButtonHyperlink(
866
+ Renderer *p, /* Renderer state */
867
+ ParsedMarkup *pMarkup, /* Potential button markup */
868
+ const char *z, /* Complete text of Wiki */
869
+ int *pN /* Characters of z[] consumed */
870
+){
871
+ const char *zClass;
872
+ const char *zHref;
873
+ char *zTag;
874
+ int i, j;
875
+ if( (p->state & WIKI_BUTTONS)==0 ) return 0;
876
+ zClass = attributeValue(pMarkup, ATTR_CLASS);
877
+ if( zClass==0 ) return 0;
878
+ if( fossil_strcmp(zClass, "button")!=0 ) return 0;
879
+ zHref = attributeValue(pMarkup, ATTR_HREF);
880
+ if( zHref==0 ) return 0;
881
+ i = *pN;
882
+ while( z[i] && z[i]!='<' ){ i++; }
883
+ if( fossil_strnicmp(&z[i], "</a>",4)!=0 ) return 0;
884
+ for(j=*pN; fossil_isspace(z[j]); j++){}
885
+ zTag = mprintf("%.*s", i-j, &z[j]);
886
+ j = (int)strlen(zTag);
887
+ while( j>0 && fossil_isspace(zTag[j-1]) ){ j--; }
888
+ if( j==0 ) return 0;
889
+ style_submenu_element(zTag, zTag, "%s", zHref);
890
+ *pN = i+4;
891
+ return 1;
892
+}
842893
843894
/*
844895
** Pop a single element off of the stack. As the element is popped,
845896
** output its end tag if it is not a </div> tag.
846897
*/
@@ -1453,14 +1504,16 @@
14531504
pushStack(p, markup.iCode);
14541505
renderMarkup(p->pOut, &markup);
14551506
}
14561507
}else
14571508
if( markup.iType==MUTYPE_HYPERLINK ){
1458
- popStackToTag(p, markup.iCode);
1459
- startAutoParagraph(p);
1460
- renderMarkup(p->pOut, &markup);
1461
- pushStack(p, markup.iCode);
1509
+ if( !isButtonHyperlink(p, &markup, z, &n) ){
1510
+ popStackToTag(p, markup.iCode);
1511
+ startAutoParagraph(p);
1512
+ renderMarkup(p->pOut, &markup);
1513
+ pushStack(p, markup.iCode);
1514
+ }
14621515
}else
14631516
{
14641517
if( markup.iType==MUTYPE_FONT ){
14651518
startAutoParagraph(p);
14661519
}else if( markup.iType==MUTYPE_BLOCK || markup.iType==MUTYPE_LIST ){
@@ -1508,11 +1561,11 @@
15081561
void wiki_convert(Blob *pIn, Blob *pOut, int flags){
15091562
char *z;
15101563
Renderer renderer;
15111564
15121565
memset(&renderer, 0, sizeof(renderer));
1513
- renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH;
1566
+ renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags;
15141567
if( flags & WIKI_NOBLOCK ){
15151568
renderer.state |= INLINE_MARKUP_ONLY;
15161569
}
15171570
if( flags & WIKI_INLINE ){
15181571
renderer.wantAutoParagraph = 0;
@@ -1538,17 +1591,25 @@
15381591
free(renderer.aStack);
15391592
}
15401593
15411594
/*
15421595
** COMMAND: test-wiki-render
1596
+**
1597
+** %fossil test-wiki-render FILE [OPTIONS]
1598
+**
1599
+** Options:
1600
+** --buttons Set the WIKI_BUTTONS flag
15431601
*/
15441602
void test_wiki_render(void){
15451603
Blob in, out;
1604
+ int flags = 0;
1605
+ if( find_option("buttons",0,0)!=0 ) flags |= WIKI_BUTTONS;
1606
+ verify_all_options();
15461607
if( g.argc!=3 ) usage("FILE");
15471608
blob_zero(&out);
15481609
blob_read_from_file(&in, g.argv[2]);
1549
- wiki_convert(&in, &out, 0);
1610
+ wiki_convert(&in, &out, flags);
15501611
blob_write_to_file(&out, "-");
15511612
}
15521613
15531614
/*
15541615
** Search for a <title>...</title> at the beginning of a wiki page.
15551616
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -27,10 +27,11 @@
27 */
28 #define WIKI_NOFOLLOW 0x001
29 #define WIKI_HTML 0x002
30 #define WIKI_INLINE 0x004 /* Do not surround with <p>..</p> */
31 #define WIKI_NOBLOCK 0x008 /* No block markup of any kind */
 
32 #endif
33
34
35 /*
36 ** These are the only markup attributes allowed.
@@ -378,19 +379,19 @@
378 #define TOKEN_INDENT 9 /* " " */
379 #define TOKEN_RAW 10 /* Output exactly (used when wiki-use-html==1) */
380 #define TOKEN_TEXT 11 /* None of the above */
381
382 /*
383 ** State flags
384 */
385 #define AT_NEWLINE 0x001 /* At start of a line */
386 #define AT_PARAGRAPH 0x002 /* At start of a paragraph */
387 #define ALLOW_WIKI 0x004 /* Allow wiki markup */
388 #define FONT_MARKUP_ONLY 0x008 /* Only allow MUTYPE_FONT markup */
389 #define INLINE_MARKUP_ONLY 0x010 /* Allow only "inline" markup */
390 #define IN_LIST 0x020 /* Within wiki <ul> or <ol> */
391 #define WIKI_USE_HTML 0x040 /* wiki-use-html option = on */
392
393 /*
394 ** Current state of the rendering engine
395 */
396 typedef struct Renderer Renderer;
@@ -825,22 +826,72 @@
825 z[n] = p->aAttr[i].cTerm;
826 }
827 }
828
829 /*
830 ** Return the ID attribute for markup. Return NULL if there is no
831 ** ID attribute.
832 */
833 static const char *markupId(ParsedMarkup *p){
834 int i;
835 for(i=0; i<p->nAttr; i++){
836 if( p->aAttr[i].iACode==ATTR_ID ){
837 return p->aAttr[i].zValue;
838 }
839 }
840 return 0;
841 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
842
843 /*
844 ** Pop a single element off of the stack. As the element is popped,
845 ** output its end tag if it is not a </div> tag.
846 */
@@ -1453,14 +1504,16 @@
1453 pushStack(p, markup.iCode);
1454 renderMarkup(p->pOut, &markup);
1455 }
1456 }else
1457 if( markup.iType==MUTYPE_HYPERLINK ){
1458 popStackToTag(p, markup.iCode);
1459 startAutoParagraph(p);
1460 renderMarkup(p->pOut, &markup);
1461 pushStack(p, markup.iCode);
 
 
1462 }else
1463 {
1464 if( markup.iType==MUTYPE_FONT ){
1465 startAutoParagraph(p);
1466 }else if( markup.iType==MUTYPE_BLOCK || markup.iType==MUTYPE_LIST ){
@@ -1508,11 +1561,11 @@
1508 void wiki_convert(Blob *pIn, Blob *pOut, int flags){
1509 char *z;
1510 Renderer renderer;
1511
1512 memset(&renderer, 0, sizeof(renderer));
1513 renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH;
1514 if( flags & WIKI_NOBLOCK ){
1515 renderer.state |= INLINE_MARKUP_ONLY;
1516 }
1517 if( flags & WIKI_INLINE ){
1518 renderer.wantAutoParagraph = 0;
@@ -1538,17 +1591,25 @@
1538 free(renderer.aStack);
1539 }
1540
1541 /*
1542 ** COMMAND: test-wiki-render
 
 
 
 
 
1543 */
1544 void test_wiki_render(void){
1545 Blob in, out;
 
 
 
1546 if( g.argc!=3 ) usage("FILE");
1547 blob_zero(&out);
1548 blob_read_from_file(&in, g.argv[2]);
1549 wiki_convert(&in, &out, 0);
1550 blob_write_to_file(&out, "-");
1551 }
1552
1553 /*
1554 ** Search for a <title>...</title> at the beginning of a wiki page.
1555
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -27,10 +27,11 @@
27 */
28 #define WIKI_NOFOLLOW 0x001
29 #define WIKI_HTML 0x002
30 #define WIKI_INLINE 0x004 /* Do not surround with <p>..</p> */
31 #define WIKI_NOBLOCK 0x008 /* No block markup of any kind */
32 #define WIKI_BUTTONS 0x010 /* Allow sub-menu buttons */
33 #endif
34
35
36 /*
37 ** These are the only markup attributes allowed.
@@ -378,19 +379,19 @@
379 #define TOKEN_INDENT 9 /* " " */
380 #define TOKEN_RAW 10 /* Output exactly (used when wiki-use-html==1) */
381 #define TOKEN_TEXT 11 /* None of the above */
382
383 /*
384 ** State flags. Save the lower 16 bits for the WIKI_* flags.
385 */
386 #define AT_NEWLINE 0x0010000 /* At start of a line */
387 #define AT_PARAGRAPH 0x0020000 /* At start of a paragraph */
388 #define ALLOW_WIKI 0x0040000 /* Allow wiki markup */
389 #define FONT_MARKUP_ONLY 0x0080000 /* Only allow MUTYPE_FONT markup */
390 #define INLINE_MARKUP_ONLY 0x0100000 /* Allow only "inline" markup */
391 #define IN_LIST 0x0200000 /* Within wiki <ul> or <ol> */
392 #define WIKI_USE_HTML 0x0400000 /* wiki-use-html option = on */
393
394 /*
395 ** Current state of the rendering engine
396 */
397 typedef struct Renderer Renderer;
@@ -825,22 +826,72 @@
826 z[n] = p->aAttr[i].cTerm;
827 }
828 }
829
830 /*
831 ** Return the value of attribute attrId. Return NULL if there is no
832 ** ID attribute.
833 */
834 static const char *attributeValue(ParsedMarkup *p, int attrId){
835 int i;
836 for(i=0; i<p->nAttr; i++){
837 if( p->aAttr[i].iACode==attrId ){
838 return p->aAttr[i].zValue;
839 }
840 }
841 return 0;
842 }
843
844 /*
845 ** Return the ID attribute for markup. Return NULL if there is no
846 ** ID attribute.
847 */
848 static const char *markupId(ParsedMarkup *p){
849 return attributeValue(p, ATTR_ID);
850 }
851
852 /*
853 ** Check markup pMarkup to see if it is a hyperlink with class "button"
854 ** that is follows by simple text and an </a> only. Example:
855 **
856 ** <a class="button" href="../index.wiki">Index</a>
857 **
858 ** If the markup matches this pattern, and if the WIKI_BUTTONS flag was
859 ** passed to wiki_convert(), then transform this link into a submenu
860 ** button, skip the text, and set *pN equal to the total length of the
861 ** text through the end of </a> and return true. If the markup does
862 ** not match or if WIKI_BUTTONS is not set, then make no changes to *pN
863 ** and return false.
864 */
865 static int isButtonHyperlink(
866 Renderer *p, /* Renderer state */
867 ParsedMarkup *pMarkup, /* Potential button markup */
868 const char *z, /* Complete text of Wiki */
869 int *pN /* Characters of z[] consumed */
870 ){
871 const char *zClass;
872 const char *zHref;
873 char *zTag;
874 int i, j;
875 if( (p->state & WIKI_BUTTONS)==0 ) return 0;
876 zClass = attributeValue(pMarkup, ATTR_CLASS);
877 if( zClass==0 ) return 0;
878 if( fossil_strcmp(zClass, "button")!=0 ) return 0;
879 zHref = attributeValue(pMarkup, ATTR_HREF);
880 if( zHref==0 ) return 0;
881 i = *pN;
882 while( z[i] && z[i]!='<' ){ i++; }
883 if( fossil_strnicmp(&z[i], "</a>",4)!=0 ) return 0;
884 for(j=*pN; fossil_isspace(z[j]); j++){}
885 zTag = mprintf("%.*s", i-j, &z[j]);
886 j = (int)strlen(zTag);
887 while( j>0 && fossil_isspace(zTag[j-1]) ){ j--; }
888 if( j==0 ) return 0;
889 style_submenu_element(zTag, zTag, "%s", zHref);
890 *pN = i+4;
891 return 1;
892 }
893
894 /*
895 ** Pop a single element off of the stack. As the element is popped,
896 ** output its end tag if it is not a </div> tag.
897 */
@@ -1453,14 +1504,16 @@
1504 pushStack(p, markup.iCode);
1505 renderMarkup(p->pOut, &markup);
1506 }
1507 }else
1508 if( markup.iType==MUTYPE_HYPERLINK ){
1509 if( !isButtonHyperlink(p, &markup, z, &n) ){
1510 popStackToTag(p, markup.iCode);
1511 startAutoParagraph(p);
1512 renderMarkup(p->pOut, &markup);
1513 pushStack(p, markup.iCode);
1514 }
1515 }else
1516 {
1517 if( markup.iType==MUTYPE_FONT ){
1518 startAutoParagraph(p);
1519 }else if( markup.iType==MUTYPE_BLOCK || markup.iType==MUTYPE_LIST ){
@@ -1508,11 +1561,11 @@
1561 void wiki_convert(Blob *pIn, Blob *pOut, int flags){
1562 char *z;
1563 Renderer renderer;
1564
1565 memset(&renderer, 0, sizeof(renderer));
1566 renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags;
1567 if( flags & WIKI_NOBLOCK ){
1568 renderer.state |= INLINE_MARKUP_ONLY;
1569 }
1570 if( flags & WIKI_INLINE ){
1571 renderer.wantAutoParagraph = 0;
@@ -1538,17 +1591,25 @@
1591 free(renderer.aStack);
1592 }
1593
1594 /*
1595 ** COMMAND: test-wiki-render
1596 **
1597 ** %fossil test-wiki-render FILE [OPTIONS]
1598 **
1599 ** Options:
1600 ** --buttons Set the WIKI_BUTTONS flag
1601 */
1602 void test_wiki_render(void){
1603 Blob in, out;
1604 int flags = 0;
1605 if( find_option("buttons",0,0)!=0 ) flags |= WIKI_BUTTONS;
1606 verify_all_options();
1607 if( g.argc!=3 ) usage("FILE");
1608 blob_zero(&out);
1609 blob_read_from_file(&in, g.argv[2]);
1610 wiki_convert(&in, &out, flags);
1611 blob_write_to_file(&out, "-");
1612 }
1613
1614 /*
1615 ** Search for a <title>...</title> at the beginning of a wiki page.
1616

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button