Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Support multiline footnote definitions and inline footnotes via <tt>^[...]</tt> syntax (this syntax is not settled yet). Fix overall link support that was broken by [e3710ccd3a5a]. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | markdown-footnotes |
| Files: | files | file ages | folders |
| SHA3-256: |
78b7846b8eff7c66df5c81a5d3bb02ea |
| User & Date: | george 2022-02-01 20:12:15.043 |
| Original Comment: | Support multiline footnote definitions (<tt>is_footnote()</tt> function) and inline footnotes via <verbatim>^[...]</verbatim> syntax (this syntax is not settled yet). Fix overall link support that was broken by [e3710ccd3a5a]. |
Context
|
2022-02-02
| ||
| 17:50 | Bug-fix: Do not loose referenced footnotes when inline footnotes are also used. ... (check-in: a8f0f1e7ff user: george tags: markdown-footnotes) | |
|
2022-02-01
| ||
| 20:12 | Support multiline footnote definitions and inline footnotes via <tt>^[...]</tt> syntax (this syntax is not settled yet). Fix overall link support that was broken by [e3710ccd3a5a]. ... (check-in: 78b7846b8e user: george tags: markdown-footnotes) | |
|
2022-01-30
| ||
| 17:38 | Do not assume little-endian architecture within <tt>to_base26()</tt> function. ... (check-in: 4b63b1ee55 user: george tags: markdown-footnotes) | |
Changes
Changes to src/backlink.c.
| ︙ | ︙ | |||
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
void markdown_extract_links(
char *zInputText,
Backlink *p
){
struct mkd_renderer html_renderer = {
/* prolog */ (void(*)(Blob*,void*))mkdn_noop0,
/* epilog */ (void(*)(Blob*,void*))mkdn_noop0,
/* blockcode */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
/* blockquote */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
/* blockhtml */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
/* header */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
/* hrule */ (void(*)(Blob*,void*))mkdn_noop0,
/* list */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
/* listitem */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
/* paragraph */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
/* table */ (void(*)(Blob*,Blob*,Blob*,void*))mkdn_noop0,
/* table_cell */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
/* table_row */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
/* autolink */ (int(*)(Blob*,Blob*,enum mkd_autolink,void*))mkdn_noop1,
/* codespan */ (int(*)(Blob*,Blob*,int,void*))mkdn_noop1,
/* dbl_emphas */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1,
/* emphasis */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1,
/* image */ (int(*)(Blob*,Blob*,Blob*,Blob*,void*))mkdn_noop1,
/* linebreak */ (int(*)(Blob*,void*))mkdn_noop1,
/* link */ backlink_md_link,
/* r_html_tag */ (int(*)(Blob*,Blob*,void*))mkdn_noop1,
/* tri_emphas */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1,
0, /* entity */
0, /* normal_text */
"*_", /* emphasis characters */
0 /* client data */
};
Blob out, in;
html_renderer.opaque = (void*)p;
| > > > > > > | 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
void markdown_extract_links(
char *zInputText,
Backlink *p
){
struct mkd_renderer html_renderer = {
/* prolog */ (void(*)(Blob*,void*))mkdn_noop0,
/* epilog */ (void(*)(Blob*,void*))mkdn_noop0,
/* footnotes */ (void(*)(Blob*,const Blob*, void*))mkdn_noop0,
/* blockcode */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
/* blockquote */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
/* blockhtml */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
/* header */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
/* hrule */ (void(*)(Blob*,void*))mkdn_noop0,
/* list */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
/* listitem */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
/* paragraph */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
/* table */ (void(*)(Blob*,Blob*,Blob*,void*))mkdn_noop0,
/* table_cell */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
/* table_row */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
/* footnoteitm*/ (void(*)(Blob*,const Blob*,int,int,void*))mkdn_noop0,
/* autolink */ (int(*)(Blob*,Blob*,enum mkd_autolink,void*))mkdn_noop1,
/* codespan */ (int(*)(Blob*,Blob*,int,void*))mkdn_noop1,
/* dbl_emphas */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1,
/* emphasis */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1,
/* image */ (int(*)(Blob*,Blob*,Blob*,Blob*,void*))mkdn_noop1,
/* linebreak */ (int(*)(Blob*,void*))mkdn_noop1,
/* link */ backlink_md_link,
/* r_html_tag */ (int(*)(Blob*,Blob*,void*))mkdn_noop1,
/* tri_emphas */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1,
/* footnoteref*/ (int(*)(Blob*,int,int,void*))mkdn_noop1,
0, /* entity */
0, /* normal_text */
"*_", /* emphasis characters */
0 /* client data */
};
Blob out, in;
html_renderer.opaque = (void*)p;
|
| ︙ | ︙ |
Changes to src/default.css.
| ︙ | ︙ | |||
1670 1671 1672 1673 1674 1675 1676 |
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 {
| | | 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 |
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;
}
div.markdown a.noteref:target > sup {
background: gold;
|
| ︙ | ︙ |
Changes to src/markdown.c.
| ︙ | ︙ | |||
1064 1065 1066 1067 1068 1069 1070 |
struct Blob *ob,
struct render *rndr,
char *data,
size_t offset,
size_t size
){
const int is_img = (offset && data[-1] == '!');
| < < > | 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 |
struct Blob *ob,
struct render *rndr,
char *data,
size_t offset,
size_t size
){
const int is_img = (offset && data[-1] == '!');
size_t i = 1, txt_e;
struct Blob *content = 0;
struct Blob *link = 0;
struct Blob *title = 0;
const struct footnote *fn = 0;
int level, ret;
/* ? FIXME: assert( size>0 ); */
/* checking whether the correct renderer exists */
if( (is_img && !rndr->make.image) || (!is_img && !rndr->make.link) ){
return 0;
}
/* looking for the matching closing bracket */
|
| ︙ | ︙ | |||
1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 |
id_data = data+1;
id_size = txt_e-1;
}else{
/* explicit id - between brackets */
id_data = data+i+1;
id_size = id_end-(i+1);
}
if( get_link_ref(rndr, link, title, id_data, id_size)<0 ){
goto char_link_cleanup;
}
i = id_end+1;
/* shortcut reference style link */
}else{
| > > > | | | > | > > | > > | > > | > > | 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 |
id_data = data+1;
id_size = txt_e-1;
}else{
/* explicit id - between brackets */
id_data = data+i+1;
id_size = id_end-(i+1);
}
if( get_link_ref(rndr, link, title, id_data, id_size)<0 ){
goto char_link_cleanup;
}
i = id_end+1;
/* shortcut reference style link */
}else{
if( offset && data[-1]=='^' ){
/* free-standing inline note */
struct footnote note = {empty_blob,empty_blob,0,0};
note.index = ++(rndr->iNotesCount);
note.nUsed = 1;
blob_append(¬e.text,data+1,txt_e-1);
blob_append(&rndr->notes, (char *)¬e, sizeof note);
fn = (struct footnote*)(blob_buffer(&rndr->notes)
+ blob_size(&rndr->notes) - sizeof(note));
}else if(!is_img && size>2 && data[1]=='^'){
/* free-standing reference */
fn = get_footnote(rndr, data+1, txt_e-1);
if( !fn ) goto char_link_cleanup;
}else if( get_link_ref(rndr, link, title, data+1, txt_e-1)<0 ){
goto char_link_cleanup;
}
/* rewinding the whitespace */
i = txt_e+1;
}
/* building content: img alt is escaped, link content is parsed */
if( txt_e>1 ){
if( is_img ) blob_append(content, data+1, txt_e-1);
else if(!fn) parse_inline(content, rndr, data+1, txt_e-1);
}
/* 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( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]=='^' ) ob->nUsed--;
/* ? FIXME: the above line looks like a hack */
if(rndr->make.footnote_ref){
ret = rndr->make.footnote_ref(ob,fn->index,fn->nUsed,rndr->make.opaque);
}
}else{
ret = rndr->make.link(ob, link, title, content, rndr->make.opaque);
}
|
| ︙ | ︙ | |||
2158 2159 2160 2161 2162 2163 2164 |
if( data[beg+3]==' ' ) return 0;
}
}
}
i += beg;
/* id part: anything but a newline between brackets */
| | > > | 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 |
if( data[beg+3]==' ' ) return 0;
}
}
}
i += beg;
/* id part: anything but a newline between brackets */
if( data[i]!='[' ) return 0;
i++;
if( i>=end || data[i]=='^' ) return 0; /* see is_footnote() */
id_offset = i;
while( i<end && data[i]!='\n' && data[i]!='\r' && data[i]!=']' ){ i++; }
if( i>=end || data[i]!=']' ) return 0;
id_end = i;
/* spacer: colon (space | tab)* newline? (space | tab)* */
i++;
|
| ︙ | ︙ | |||
2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 |
&& data[i]!=' '
&& data[i]!='\t'
&& data[i]!='\n'
&& data[i]!='\r'
){
i += 1;
}
if( data[i-1]=='>' ) link_end = i-1; else link_end = i;
/* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */
while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }
if( i<end
&& data[i]!='\n'
&& data[i]!='\r'
| > | 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 |
&& data[i]!=' '
&& data[i]!='\t'
&& data[i]!='\n'
&& data[i]!='\r'
){
i += 1;
}
/* ? FIXME: if( data[i-1]=='>' && data[link_offset-1]!='<' ) */
if( data[i-1]=='>' ) link_end = i-1; else link_end = i;
/* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */
while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }
if( i<end
&& data[i]!='\n'
&& data[i]!='\r'
|
| ︙ | ︙ | |||
2251 2252 2253 2254 2255 2256 2257 | return 1; } /********************* * FOOTNOTE PARSING * *********************/ | | > < | < < < > | | < > | > | > > | | < > > > > > > > > > > > > | > > > > > > > > < | < > | > | > | > > | > > > | > > > | > | > > | > > > > > | | | | < < < | | 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 |
return 1;
}
/*********************
* FOOTNOTE PARSING *
*********************/
/* is_footnote -- check if data holds a definition of a labeled footnote.
* If so then append the corresponding element to `footnotes` array */
static int is_footnote(
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 };
/* 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;
i++;
if( data[i]!='^' ) return 0;
id_offset = i++;
/* id part: anything but a newline between brackets */
while( i<end && data[i]!=']' && data[i]!='\n' && data[i]!='\r' ){ i++; }
if( i>=end || data[i]!=']' ) return 0;
id_end = i++;
/* spacer: colon (space | tab)* */
if( i>=end || data[i]!=':' ) return 0;
i++;
while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }
/* passthrough truncated footnote definition
* FIXME: maybe omit it? */
if( i>=end ) return 0;
if( build_ref_id(&fn.id, data+id_offset, id_end-id_offset)<0 ) return 0;
/* footnote's text may start on the same line */
if( data[i]!='\n' && data[i]!='\r' ){
const size_t j = i;
do i++; while( i<end && data[i]!='\n' && data[i]!='\r' );
blob_append(&fn.text, data+j, i-j);
if( i<end ){
blob_append_char(&fn.text, data[i]);
i++;
if( i<end && data[i]=='\r' && data[i-1] == '\n' ){
blob_append_char(&fn.text, data[i]);
i++;
}
}
}else{
i++;
if( i<end && data[i]=='\r' && data[i-1] == '\n' ) i++;
}
if( i<end ){
/* compute the indentation from the 2nd line */
size_t indent = i;
const char *spaces = data+i;
while( i<end && data[i]==' ' ){ i++; }
if( i>=end ) goto footnote_finish;
indent = i - indent;
i -= indent;
if( indent<2 ) goto footnote_finish;
/* process the 2nd and the following lines */
while( i+indent<end && memcmp(data+i,spaces,indent)==0 ){
size_t j;
i += indent;
j = i;
while( i<end && data[i]!='\n' && data[i]!='\r' ) i++;
blob_append(&fn.text, data+j, i-j);
if( i>=end ) break;
blob_append_char(&fn.text, data[i]);
i++;
if( i<end && data[i]=='\r' && data[i-1] == '\n' ){
blob_append_char(&fn.text, data[i]);
i++;
}
}
}
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 ) blob_append(footnotes, (char *)&fn, sizeof fn);
return 1;
}
/**********************
* EXPORTED FUNCTIONS *
**********************/
|
| ︙ | ︙ | |||
2432 2433 2434 2435 2436 2437 2438 |
blob_reset(&lr[i].id);
blob_reset(&lr[i].link);
blob_reset(&lr[i].title);
}
blob_reset(&rndr.refs);
end = COUNT_FOOTNOTES(&rndr.notes);
for(i=0; i<end; i++){
| | | 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 |
blob_reset(&lr[i].id);
blob_reset(&lr[i].link);
blob_reset(&lr[i].title);
}
blob_reset(&rndr.refs);
end = COUNT_FOOTNOTES(&rndr.notes);
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);
for(i=0; i<rndr.nBlobCache; i++){
fossil_free(rndr.aBlobCache[i]);
}
}
|
Changes to src/markdown_html.c.
| ︙ | ︙ | |||
377 378 379 380 381 382 383 |
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," …");
}
| | | 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 |
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);
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) ){
|
| ︙ | ︙ |