Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Handle misreferences more thoroughly. Implement support of footnotes-within-footnotes with (hopefully) proper crosslinking (that's where it's getting tricky). |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | markdown-footnotes |
Files: | files | file ages | folders |
SHA3-256: |
1787f6df11bbbe4fd84a1fb5c219244d |
User & Date: | george 2022-02-06 22:53:22 |
Context
2022-02-08
| ||
13:39 | Add const qualifier to the arguments of the blob_compare() function. ... (check-in: 2822b63b user: george tags: markdown-footnotes) | |
2022-02-06
| ||
22:53 | Handle misreferences more thoroughly. Implement support of footnotes-within-footnotes with (hopefully) proper crosslinking (that's where it's getting tricky). ... (check-in: 1787f6df user: george tags: markdown-footnotes) | |
2022-02-04
| ||
23:07 | Handle misreferences: a reference to undefined footnote. ... (check-in: 28e6a9cd user: george tags: markdown-footnotes) | |
Changes
Changes to src/default.css.
︙ | ︙ | |||
1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 | font-family: monospace; } div.content div.markdown > ol.footnotes { font-size: 90%; } div.content div.markdown > ol.footnotes > li { margin-bottom: 0.5em; } div.content div.markdown > ol.footnotes > li > .footnote-backrefs { margin-right: 0.5em; font-weight: bold; } div.markdown > ol.footnotes > li > .footnote-backrefs > a:target { background: gold; } | > > > > > > > > > > | | > | | | 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 | font-family: monospace; } div.content div.markdown > ol.footnotes { font-size: 90%; } div.content div.markdown > ol.footnotes > li { margin-bottom: 0.5em; } div.markdown ol.footnotes > li.unreferenced-footnote, div.markdown ol.footnotes > li.misreferences { background: #ffdddd; } div.markdown ol.footnotes > li.unreferenced-footnote:first-child, div.markdown ol.footnotes > li.misreferences { margin-top: 0.75em; padding-top: 0.25em; padding-bottom: 0.25em; } div.content div.markdown > ol.footnotes > li > .footnote-backrefs { margin-right: 0.5em; font-weight: bold; } div.markdown > ol.footnotes > li > .footnote-backrefs > a:target { background: gold; } div.markdown sup.noteref > a:target { background: gold; } div.markdown sup.noteref.misref, div.markdown sup.noteref.misref > a { color: red; font-size: 90%; } div.markdown span.notescope:hover, div.markdown span.notescope:target { border-bottom: 2px solid gold; } div.markdown span.notescope:hover > sup.noteref > a, div.markdown span.notescope:target > sup.noteref > a { background: gold; } /* Objects in the "desktoponly" class are invisible on mobile */ @media screen and (max-width: 600px) { .desktoponly { display: none; |
︙ | ︙ |
Changes to src/markdown.c.
︙ | ︙ | |||
125 126 127 128 129 130 131 | struct Blob *ob, const struct Blob *ib, const struct mkd_renderer *rndr); #endif /* INTERFACE */ | | | > | | > | > > > | | 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | struct Blob *ob, const struct Blob *ib, const struct mkd_renderer *rndr); #endif /* INTERFACE */ #define BLOB_COUNT(pBlob,el_type) (blob_size(pBlob)/sizeof(el_type)) #define COUNT_FOOTNOTES(pBlob) BLOB_COUNT(pBlob,struct footnote) #define CAST_AS_FOOTNOTES(pBlob) ((struct footnote*)blob_buffer(pBlob)) /*************** * LOCAL TYPES * ***************/ /* link_ref -- reference to a link */ struct link_ref { struct Blob id; /* must be the first field as in footnote struct */ struct Blob link; struct Blob title; }; struct footnote { struct Blob id; /* must be the first field as in link_ref struct */ struct Blob text; /* footnote's content that is rendered at the end */ int bRndred; /* indicates if `text` holds a rendered content */ int defno; /* serial number of definition, set during the first pass */ int index; /* set to the index within array after ordering by id */ int iMark; /* user-visible numeric marker, assigned upon the first use*/ int nUsed; /* counts references to this note, increments upon each use*/ }; /* char_trigger -- function pointer to render active chars */ /* returns the number of chars taken care of */ /* data is the pointer of the beginning of the span */ /* offset is the number of valid chars before data */ |
︙ | ︙ | |||
172 173 174 175 176 177 178 | int iDepth; /* Depth of recursion */ int nBlobCache; /* Number of entries in aBlobCache */ struct Blob *aBlobCache[20]; /* Cache of Blobs available for reuse */ struct { Blob all; /* array of footnotes */ int nLbled; /* number of labeled footnotes found during the first pass */ | | | < | 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | int iDepth; /* Depth of recursion */ int nBlobCache; /* Number of entries in aBlobCache */ struct Blob *aBlobCache[20]; /* Cache of Blobs available for reuse */ struct { Blob all; /* array of footnotes */ int nLbled; /* number of labeled footnotes found during the first pass */ int nMarks; /* counts distinct indices found during the second pass */ struct footnote misref; /* nUsed counts misreferences, index must be -1 */ } notes; }; /* html_tag -- structure for quick HTML tag search (inspired from discount) */ struct html_tag { const char *text; int size; |
︙ | ︙ | |||
201 202 203 204 205 206 207 | ** in any way. */ static const struct html_tag block_tags[] = { { "html", 4 }, { "pre", 3 }, { "script", 6 }, }; | < | 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | ** in any way. */ static const struct html_tag block_tags[] = { { "html", 4 }, { "pre", 3 }, { "script", 6 }, }; /*************************** * STATIC HELPER FUNCTIONS * ***************************/ /* build_ref_id -- collapse whitespace from input text to make it a ref id */ /* FIXME: does this function handle non-Unix newlines? */ |
︙ | ︙ | |||
268 269 270 271 272 273 274 | static int cmp_link_ref_sort(const void *a, const void *b){ struct link_ref *lra = (void *)a; struct link_ref *lrb = (void *)b; return blob_compare(&lra->id, &lrb->id); } /* cmp_footnote_sort -- comparison function for footnotes qsort. | | > | | > | > > > > > | > > > > | > > > | > | | 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 | static int cmp_link_ref_sort(const void *a, const void *b){ struct link_ref *lra = (void *)a; struct link_ref *lrb = (void *)b; return blob_compare(&lra->id, &lrb->id); } /* cmp_footnote_sort -- comparison function for footnotes qsort. * Unreferenced footnotes (when nUsed == 0) sort last and * are sorted in the order of definition in the source */ static int cmp_footnote_sort(const void *fna, const void *fnb){ const struct footnote *a = fna, *b = fnb; int i, j; assert( a->nUsed >= 0 ); assert( b->nUsed >= 0 ); assert( a->defno >= 0 ); assert( b->defno >= 0 ); if( a->nUsed ){ assert( a->iMark > 0 ); if( !b->nUsed ) return -1; assert( b->iMark > 0 ); i = a->iMark; j = b->iMark; }else{ if( b->nUsed ) return 1; i = a->defno; j = b->defno; } if( i < j ) return -1; if( i > j ) return 1; return 0; } /* cmp_html_tag -- comparison function for bsearch() (stolen from discount) */ static int cmp_html_tag(const void *a, const void *b){ const struct html_tag *hta = a; const struct html_tag *htb = b; int sz = hta->size; |
︙ | ︙ | |||
1050 1051 1052 1053 1054 1055 1056 | if( build_ref_id(id, data, size)<0 ) goto cleanup; fn = bsearch(id, blob_buffer(&rndr->notes.all), rndr->notes.nLbled, sizeof (struct footnote), cmp_link_ref); if( !fn ) goto cleanup; | | | | | | | | > | | 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 | if( build_ref_id(id, data, size)<0 ) goto cleanup; fn = bsearch(id, blob_buffer(&rndr->notes.all), rndr->notes.nLbled, sizeof (struct footnote), cmp_link_ref); if( !fn ) goto cleanup; if( fn->nUsed == 0 ){ /* the first reference to the footnote */ assert( fn->iMark == 0 ); fn->iMark = ++(rndr->notes.nMarks); } fn->nUsed++; assert( fn->iMark > 0 ); assert( fn->nUsed > 0 ); cleanup: release_work_buffer( rndr, id ); return fn; } /* Adds unlabeled footnote to the rndr. * If text is blank then returns 0, * otherwise returns the address of the added footnote. */ static inline const struct footnote* add_inline_footnote( struct render *rndr, const char *text, size_t size ){ struct footnote fn = { empty_blob, empty_blob, 0, 0, 0, 0, 0 }; while(size && (*text==' ' || *text=='\t')){ text++; size--; } if(!size) return 0; fn.iMark = ++(rndr->notes.nMarks); fn.nUsed = 1; fn.index = COUNT_FOOTNOTES(&rndr->notes.all); assert( fn.iMark > 0 ); blob_append(&fn.text, text, size); blob_append(&rndr->notes.all, (char *)&fn, sizeof fn); return (struct footnote*)( blob_buffer(&rndr->notes.all) +( blob_size(&rndr->notes.all)-sizeof fn )); } /* Return the offset of the matching closing bracket or 0 if not found. |
︙ | ︙ | |||
1118 1119 1120 1121 1122 1123 1124 | size_t end; const struct footnote* fn; if( size<4 || data[1]!='^' || !rndr->make.footnote_ref ) return 0; end = matching_bracket_offset(data, data+size); if( !end ) return 0; fn = add_inline_footnote(rndr, data+2, end-2); | | | 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 | size_t end; const struct footnote* fn; if( size<4 || data[1]!='^' || !rndr->make.footnote_ref ) return 0; end = matching_bracket_offset(data, data+size); if( !end ) return 0; fn = add_inline_footnote(rndr, data+2, end-2); if(fn) rndr->make.footnote_ref(ob,0,fn->iMark,1,rndr->make.opaque); return end+1; } /* char_link -- '[': parsing a link or an image */ static size_t char_link( struct Blob *ob, struct render *rndr, |
︙ | ︙ | |||
1223 1224 1225 1226 1227 1228 1229 | id_size--; } } if( bFootnote ){ fn = get_footnote(rndr, id_data, id_size); if( !fn ) { | | | | | | 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 | id_size--; } } if( bFootnote ){ fn = get_footnote(rndr, id_data, id_size); if( !fn ) { rndr->notes.misref.nUsed++; fn = &rndr->notes.misref; } }else if( get_link_ref(rndr, link, title, id_data, id_size)<0 ){ goto char_link_cleanup; } i = id_end+1; /* shortcut reference style link or free-standing footnote refernece */ }else{ if(!is_img && size>3 && data[1]=='^'){ /* free-standing footnote reference */ fn = get_footnote(rndr, data+2, txt_e-2); if( !fn ) { rndr->notes.misref.nUsed++; fn = &rndr->notes.misref; } release_work_buffer(rndr, content); content = 0; }else if( get_link_ref(rndr, link, title, data+1, txt_e-1)<0 ){ goto char_link_cleanup; } |
︙ | ︙ | |||
1263 1264 1265 1266 1267 1268 1269 | /* calling the relevant rendering function */ if( is_img ){ if( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]=='!' ) ob->nUsed--; ret = rndr->make.image(ob, link, title, content, rndr->make.opaque); }else if(fn){ if(rndr->make.footnote_ref){ | | | 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 | /* calling the relevant rendering function */ if( is_img ){ if( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]=='!' ) ob->nUsed--; ret = rndr->make.image(ob, link, title, content, rndr->make.opaque); }else if(fn){ if(rndr->make.footnote_ref){ ret = rndr->make.footnote_ref(ob, content, fn->iMark, fn->nUsed, rndr->make.opaque); } }else{ ret = rndr->make.link(ob, link, title, content, rndr->make.opaque); } /* cleanup */ |
︙ | ︙ | |||
2357 2358 2359 2360 2361 2362 2363 | const char *data, /* input text */ size_t beg, /* offset of the beginning of the line */ size_t end, /* offset of the end of the text */ size_t *last, /* last character of the link */ struct Blob * footnotes ){ size_t i, id_offset, id_end; | | | 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 | const char *data, /* input text */ size_t beg, /* offset of the beginning of the line */ size_t end, /* offset of the end of the text */ size_t *last, /* last character of the link */ struct Blob * footnotes ){ size_t i, id_offset, id_end; struct footnote fn = { empty_blob, empty_blob, 0, 0, 0, 0, 0 }; /* failfast if data is too short */ if( beg+5>=end ) return 0; i = beg; /* footnote definition must start at the begining of a line */ if( data[i]!='[' ) return 0; |
︙ | ︙ | |||
2436 2437 2438 2439 2440 2441 2442 | footnote_finish: if( !blob_size(&fn.text) ){ blob_reset(&fn.id); return 0; } /* a valid note has been found */ if( last ) *last = i; | > > | > | 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 | footnote_finish: if( !blob_size(&fn.text) ){ blob_reset(&fn.id); return 0; } /* a valid note has been found */ if( last ) *last = i; if( footnotes ){ fn.defno = COUNT_FOOTNOTES( footnotes ); blob_append(footnotes, (char *)&fn, sizeof fn); } return 1; } /********************** * EXPORTED FUNCTIONS * **********************/ |
︙ | ︙ | |||
2464 2465 2466 2467 2468 2469 2470 | if( !rndrer ) return; rndr.make = *rndrer; rndr.nBlobCache = 0; rndr.iDepth = 0; rndr.refs = empty_blob; rndr.notes.all = empty_blob; rndr.notes.nMarks = 0; | | | | | | 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 | if( !rndrer ) return; rndr.make = *rndrer; rndr.nBlobCache = 0; rndr.iDepth = 0; rndr.refs = empty_blob; rndr.notes.all = empty_blob; rndr.notes.nMarks = 0; rndr.notes.misref.id = empty_blob; rndr.notes.misref.text = empty_blob; rndr.notes.misref.nUsed = 0; rndr.notes.misref.iMark = -1; for(i=0; i<256; i++) rndr.active_char[i] = 0; if( (rndr.make.emphasis || rndr.make.double_emphasis || rndr.make.triple_emphasis) && rndr.make.emph_chars ){ |
︙ | ︙ | |||
2525 2526 2527 2528 2529 2530 2531 | blob_size(&rndr.refs)/sizeof(struct link_ref), sizeof(struct link_ref), cmp_link_ref_sort); } rndr.notes.nLbled = COUNT_FOOTNOTES(&rndr.notes.all); /* sorting the footnotes array by id */ if( rndr.notes.nLbled ){ | > | | > > > > | | > | | > > > > > > > > > > > > | | > > | | > > | > > > > > > | > | > > > > | > > > > > > > > > > > | > | > > | | > | > | 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 | blob_size(&rndr.refs)/sizeof(struct link_ref), sizeof(struct link_ref), cmp_link_ref_sort); } rndr.notes.nLbled = COUNT_FOOTNOTES(&rndr.notes.all); /* sorting the footnotes array by id */ if( rndr.notes.nLbled ){ fn = CAST_AS_FOOTNOTES(&rndr.notes.all); qsort(fn, rndr.notes.nLbled, sizeof(struct footnote), cmp_link_ref_sort); for(i=0; i<rndr.notes.nLbled; i++){ fn[i].index = i; } /* FIXME: handle footnotes with duplicated labels */ } /* second pass: actual rendering */ if( rndr.make.prolog ) rndr.make.prolog(ob, rndr.make.opaque); parse_block(ob, &rndr, blob_buffer(&text), blob_size(&text)); if( (blob_size(&rndr.notes.all) || rndr.notes.misref.nUsed) ){ /* Footnotes must be parsed for the correct discovery of (back)links */ Blob *notes = new_work_buffer( &rndr ); Blob *tmp = new_work_buffer( &rndr ); const struct Blob *origin = &rndr.notes.all; int nMarks = -1; /* inline notes may get appended to rndr.notes.all while rendering */ while(1){ struct footnote *aNotes; const int N = COUNT_FOOTNOTES(origin); /* make a shallow copy of `origin` */ blob_truncate(notes,0); blob_append(notes, blob_buffer(origin), blob_size(origin)); aNotes = CAST_AS_FOOTNOTES(notes); qsort(aNotes, N, sizeof(struct footnote), cmp_footnote_sort); if( nMarks == rndr.notes.nMarks ) break; nMarks = rndr.notes.nMarks; for(i=0; i<N; i++){ const int j = aNotes[i].index; struct footnote *x = CAST_AS_FOOTNOTES(origin) + j; assert( 0<=j && j<N ); if( x->bRndred || !x->nUsed ) continue; assert( x->iMark > 0 ); assert( blob_size(&x->text) ); blob_truncate(tmp,0); /* `origin` may be altered and extended through this call */ parse_inline(tmp, &rndr, blob_buffer(&x->text), blob_size(&x->text)); blob_truncate(&x->text,0); blob_append(&x->text, blob_buffer(tmp), blob_size(tmp)); x->bRndred = 1; } } release_work_buffer(&rndr,tmp); /* footnotes rendering */ if( rndr.make.footnote_item && rndr.make.footnotes ){ Blob *all_items = new_work_buffer(&rndr); for(i=0; i<COUNT_FOOTNOTES(notes); i++){ const struct footnote* x = CAST_AS_FOOTNOTES(notes) + i; if( x->bRndred ){ rndr.make.footnote_item(all_items, &x->text, x->iMark, x->nUsed, rndr.make.opaque); } } if( rndr.notes.misref.nUsed ){ rndr.make.footnote_item(all_items, 0, -1, rndr.notes.misref.nUsed, rndr.make.opaque); } /* TODO: handle unreferenced (defined but not used) footnotes */ rndr.make.footnotes(ob, all_items, rndr.make.opaque); release_work_buffer(&rndr, all_items); } release_work_buffer(&rndr, notes); } if( rndr.make.epilog ) rndr.make.epilog(ob, rndr.make.opaque); /* clean-up */ assert( rndr.iDepth==0 ); blob_reset(&text); lr = (struct link_ref *)blob_buffer(&rndr.refs); end = blob_size(&rndr.refs)/sizeof(struct link_ref); for(i=0; i<end; i++){ blob_reset(&lr[i].id); blob_reset(&lr[i].link); blob_reset(&lr[i].title); } blob_reset(&rndr.refs); fn = CAST_AS_FOOTNOTES(&rndr.notes.all); end = COUNT_FOOTNOTES(&rndr.notes.all); for(i=0; i<end; i++){ if(blob_size(&fn[i].id)) blob_reset(&fn[i].id); blob_reset(&fn[i].text); } blob_reset(&rndr.notes.all); for(i=0; i<rndr.nBlobCache; i++){ fossil_free(rndr.aBlobCache[i]); } } |
Changes to src/markdown_html.c.
︙ | ︙ | |||
323 324 325 326 327 328 329 | ){ BLOB_APPEND_LITERAL(ob, " <tr>\n"); BLOB_APPEND_BLOB(ob, cells); BLOB_APPEND_LITERAL(ob, " </tr>\n"); } static int html_footnote_ref( | | | < < < | | | < > > > > | | | | > > > > | | < | | | | > | | | > | | > | | < < < < | > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > | 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 | ){ BLOB_APPEND_LITERAL(ob, " <tr>\n"); BLOB_APPEND_BLOB(ob, cells); BLOB_APPEND_LITERAL(ob, " </tr>\n"); } static int html_footnote_ref( struct Blob *ob, const struct Blob *span, int iMark, int locus, void *opaque ){ const struct MarkdownToHtml* ctx = (struct MarkdownToHtml*)opaque; const bitfield64_t l = to_base26(locus-1,0); char pos[32]; memset(pos,0,32); assert( locus > 0 ); /* expect BUGs if the following yields compiler warnings */ if( iMark > 0 ){ /* a regular reference to a footnote */ sprintf(pos, "%s-%i-%s", ctx->unique.c, iMark, l.c); if(span && blob_size(span)) { BLOB_APPEND_LITERAL(ob,"<span class='notescope' id='noteref"); blob_appendf(ob,"%s'>",pos); BLOB_APPEND_BLOB(ob, span); blob_trim(ob); BLOB_APPEND_LITERAL(ob,"<sup class='noteref'><a href='#footnote"); blob_appendf(ob,"%s'>%i</a></sup></span>", pos, iMark); }else{ blob_trim(ob); BLOB_APPEND_LITERAL(ob,"<sup class='noteref'><a href='#footnote"); blob_appendf(ob,"%s' id='noteref%s'>%i</a></sup>", pos, pos, iMark); } }else{ /* misreference */ assert( iMark == -1 ); sprintf(pos, "%s-%s", ctx->unique.c, l.c); if(span && blob_size(span)) { blob_appendf(ob, "<span class='notescope' id='misref%s'>", pos); BLOB_APPEND_BLOB(ob, span); blob_trim(ob); BLOB_APPEND_LITERAL(ob, "<sup class='noteref misref'><a href='#misreference"); blob_appendf(ob, "%s'>misref</a></sup></span>", pos); }else{ blob_trim(ob); BLOB_APPEND_LITERAL(ob, "<sup class='noteref misref'><a href='#misreference"); blob_appendf(ob, "%s' id='misref%s'>", pos, pos); BLOB_APPEND_LITERAL(ob, "misref</a></sup>"); } } return 1; } /* Render a single item of the footnotes list. * Each backref gets a unique id to enable dynamic styling. */ static void html_footnote_item( struct Blob *ob, const struct Blob *text, int iMark, int nUsed, void *opaque ){ const char * const unique = ((struct MarkdownToHtml*)opaque)->unique.c; assert( nUsed >= 0 ); /* expect BUGs if the following yields compiler warnings */ if( iMark < 0 ){ /* misreferences */ assert( iMark == -1 ); if( !nUsed ) return; BLOB_APPEND_LITERAL(ob,"<li class='misreferences'>" "<sup class='footnote-backrefs'>"); if( nUsed == 1 ){ blob_appendf(ob,"<a id='misreference%s-a' " "href='#misref%s-a'>^</a>", unique, unique); }else{ int i; blob_append_char(ob, '^'); for(i=0; i<nUsed && i<26; i++){ const int c = i + (unsigned)'a'; blob_appendf(ob," <a id='misreference%s-%c' " "href='#misref%s-%c'>%c</a>", unique,c, unique,c, c); } if( i < nUsed ) BLOB_APPEND_LITERAL(ob," …"); } BLOB_APPEND_LITERAL(ob,"</sup>\nMisreference: use of undefined label."); }else if( nUsed ){ /* a regular footnote */ char pos[24]; assert( text ); assert( blob_size(text) ); memset(pos,0,24); sprintf(pos, "%s-%i", unique, iMark); blob_appendf(ob, "<li id='footnote%s'>", pos); BLOB_APPEND_LITERAL(ob,"<sup class='footnote-backrefs'>"); if( nUsed <= 1 ){ blob_appendf(ob,"<a id='footnote%s-a' " "href='#noteref%s-a'>^</a>", pos, pos); }else{ int i; blob_append_char(ob, '^'); for(i=0; i<nUsed && i<26; i++){ const int c = i + (unsigned)'a'; blob_appendf(ob," <a id='footnote%s-%c'" " href='#noteref%s-%c'>%c</a>", pos,c, pos,c, c); } /* It's unlikely that so many backrefs will be usefull */ /* but maybe for some machine generated documents... */ for(; i<nUsed && i<676; i++){ const bitfield64_t l = to_base26(i,0); blob_appendf(ob," <a id='footnote%s-%s'" " href='#noteref%s-%s'>%s</a>", pos,l.c, pos,l.c, l.c); } if( i < nUsed ) BLOB_APPEND_LITERAL(ob," …"); } BLOB_APPEND_LITERAL(ob,"</sup>\n"); BLOB_APPEND_BLOB(ob, text); }else{ /* a footnote was defined but wasn't used */ assert( text ); assert( blob_size(text) ); /* FIXME: not yet implemented */ return; BLOB_APPEND_LITERAL(ob, "<li class='unreferenced-footnote' id='unreferenced-footnote"); blob_appendf(ob,"%s-%i'>\n", unique, iMark); BLOB_APPEND_BLOB(ob, text); } BLOB_APPEND_LITERAL(ob, "\n</li>\n"); } static void html_footnotes( struct Blob *ob, const struct Blob *items, void *opaque ){ if( items && blob_size(items) ){ BLOB_APPEND_LITERAL(ob, |
︙ | ︙ |
Changes to test/markdown-test3.md.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | Markdown Footnotes Test Document ================================ **This document** should help with testing of footnotes support that is introduced by the ["`markdown-footnotes`"][branch] branch. It **might look pretty misformatted unless rendered by the proper Fossil executable that incorporates the abovementioned branch.**[^1] Developers are invited to add test cases here[^here]. It is suggested that the more simple is a test case the earlier it should appear in this document.[^ if glitch occurs ] A footnote's label should be case insensitive[^ case INSENSITIVE ], it is whitespace-savvy and can even contain newlines.[^ a multiline label] A labeled footnote may be [referenced several times][^many-refs]. A footnote's text should support Markdown [markup][^]. Another reference[^many-refs] to the preveously used footnote. Inline footnotes are supported.(^These may be usefull for adding <s>small</s> comments.) | > > > > > > > > > | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | Markdown Footnotes Test Document ================================ **This document** should help with testing of footnotes support that is introduced by the ["`markdown-footnotes`"][branch] branch. It **might look pretty misformatted unless rendered by the proper Fossil executable that incorporates the abovementioned branch.**[^1] Developers are invited to add test cases here[^here]. It is suggested that the more simple is a test case the earlier it should appear in this document.[^ if glitch occurs ] [^lost3]: This note was defined at the begining of the document. A footnote's label should be case insensitive[^ case INSENSITIVE ], it is whitespace-savvy and can even contain newlines.[^ a multiline label] A labeled footnote may be [referenced several times][^many-refs]. A footnote's text should support Markdown [markup][^]. Another reference[^many-refs] to the preveously used footnote. [^lost2]: This note was defined in the middle of the document. It references [its previous][^lost3] and [the forthcoming][^lost1] siblings. [^i am unreferenced]: If this is rendered wihin footnotes, then there is a BUG! Inline footnotes are supported.(^These may be usefull for adding <s>small</s> comments.) If [undefined label is used][^] then red "`misref`" is emited instead of a numeric marker.[^ see it yourself ] This can be overridden by the skin though. The refenrence at the end of this sentence is the sole reason of rendering of <s>`lost1` and</s> [lost2][^]. ## Footnotes [branch]: /timeline?r=markdown-footnotes&nowiki [^ 1]: Footnotes is a Fossil' extention of Markdown. Your other tools may have limited support for these. |
︙ | ︙ | |||
48 49 50 51 52 53 54 | [^ a multiline label ]: But at a footnote's definition it should still be written within square brackets on a single line. [^many-refs]: Each letter on the left is a back-reference to the place of use. | | > > > > > > | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | [^ a multiline label ]: But at a footnote's definition it should still be written within square brackets on a single line. [^many-refs]: Each letter on the left is a back-reference to the place of use. Highlighted back-reference indicates a place from which navigation occurred[^lost1]. [^lost1]: This note was defined at the end of the document. It defines an inline note. (^This is inline note defined inside of [a labeled note][^lost1].) [^markup]: E.g. *emphasis*, and [so on](/md_rules). [^undefined label is used]: For example due to a typo. |