Fossil SCM

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].

george 2022-02-01 20:12 markdown-footnotes
Commit 78b7846b8eff7c66df5c81a5d3bb02ea071ca97f2474f4c29cc30963be76de20
--- src/backlink.c
+++ src/backlink.c
@@ -255,10 +255,12 @@
255255
Backlink *p
256256
){
257257
struct mkd_renderer html_renderer = {
258258
/* prolog */ (void(*)(Blob*,void*))mkdn_noop0,
259259
/* epilog */ (void(*)(Blob*,void*))mkdn_noop0,
260
+ /* footnotes */ (void(*)(Blob*,const Blob*, void*))mkdn_noop0,
261
+
260262
/* blockcode */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
261263
/* blockquote */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
262264
/* blockhtml */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
263265
/* header */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
264266
/* hrule */ (void(*)(Blob*,void*))mkdn_noop0,
@@ -266,19 +268,23 @@
266268
/* listitem */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
267269
/* paragraph */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
268270
/* table */ (void(*)(Blob*,Blob*,Blob*,void*))mkdn_noop0,
269271
/* table_cell */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
270272
/* table_row */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
273
+ /* footnoteitm*/ (void(*)(Blob*,const Blob*,int,int,void*))mkdn_noop0,
274
+
271275
/* autolink */ (int(*)(Blob*,Blob*,enum mkd_autolink,void*))mkdn_noop1,
272276
/* codespan */ (int(*)(Blob*,Blob*,int,void*))mkdn_noop1,
273277
/* dbl_emphas */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1,
274278
/* emphasis */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1,
275279
/* image */ (int(*)(Blob*,Blob*,Blob*,Blob*,void*))mkdn_noop1,
276280
/* linebreak */ (int(*)(Blob*,void*))mkdn_noop1,
277281
/* link */ backlink_md_link,
278282
/* r_html_tag */ (int(*)(Blob*,Blob*,void*))mkdn_noop1,
279283
/* tri_emphas */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1,
284
+ /* footnoteref*/ (int(*)(Blob*,int,int,void*))mkdn_noop1,
285
+
280286
0, /* entity */
281287
0, /* normal_text */
282288
"*_", /* emphasis characters */
283289
0 /* client data */
284290
};
285291
--- src/backlink.c
+++ src/backlink.c
@@ -255,10 +255,12 @@
255 Backlink *p
256 ){
257 struct mkd_renderer html_renderer = {
258 /* prolog */ (void(*)(Blob*,void*))mkdn_noop0,
259 /* epilog */ (void(*)(Blob*,void*))mkdn_noop0,
 
 
260 /* blockcode */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
261 /* blockquote */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
262 /* blockhtml */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
263 /* header */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
264 /* hrule */ (void(*)(Blob*,void*))mkdn_noop0,
@@ -266,19 +268,23 @@
266 /* listitem */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
267 /* paragraph */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
268 /* table */ (void(*)(Blob*,Blob*,Blob*,void*))mkdn_noop0,
269 /* table_cell */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
270 /* table_row */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
 
 
271 /* autolink */ (int(*)(Blob*,Blob*,enum mkd_autolink,void*))mkdn_noop1,
272 /* codespan */ (int(*)(Blob*,Blob*,int,void*))mkdn_noop1,
273 /* dbl_emphas */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1,
274 /* emphasis */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1,
275 /* image */ (int(*)(Blob*,Blob*,Blob*,Blob*,void*))mkdn_noop1,
276 /* linebreak */ (int(*)(Blob*,void*))mkdn_noop1,
277 /* link */ backlink_md_link,
278 /* r_html_tag */ (int(*)(Blob*,Blob*,void*))mkdn_noop1,
279 /* tri_emphas */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1,
 
 
280 0, /* entity */
281 0, /* normal_text */
282 "*_", /* emphasis characters */
283 0 /* client data */
284 };
285
--- src/backlink.c
+++ src/backlink.c
@@ -255,10 +255,12 @@
255 Backlink *p
256 ){
257 struct mkd_renderer html_renderer = {
258 /* prolog */ (void(*)(Blob*,void*))mkdn_noop0,
259 /* epilog */ (void(*)(Blob*,void*))mkdn_noop0,
260 /* footnotes */ (void(*)(Blob*,const Blob*, void*))mkdn_noop0,
261
262 /* blockcode */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
263 /* blockquote */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
264 /* blockhtml */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
265 /* header */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
266 /* hrule */ (void(*)(Blob*,void*))mkdn_noop0,
@@ -266,19 +268,23 @@
268 /* listitem */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
269 /* paragraph */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
270 /* table */ (void(*)(Blob*,Blob*,Blob*,void*))mkdn_noop0,
271 /* table_cell */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
272 /* table_row */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
273 /* footnoteitm*/ (void(*)(Blob*,const Blob*,int,int,void*))mkdn_noop0,
274
275 /* autolink */ (int(*)(Blob*,Blob*,enum mkd_autolink,void*))mkdn_noop1,
276 /* codespan */ (int(*)(Blob*,Blob*,int,void*))mkdn_noop1,
277 /* dbl_emphas */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1,
278 /* emphasis */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1,
279 /* image */ (int(*)(Blob*,Blob*,Blob*,Blob*,void*))mkdn_noop1,
280 /* linebreak */ (int(*)(Blob*,void*))mkdn_noop1,
281 /* link */ backlink_md_link,
282 /* r_html_tag */ (int(*)(Blob*,Blob*,void*))mkdn_noop1,
283 /* tri_emphas */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1,
284 /* footnoteref*/ (int(*)(Blob*,int,int,void*))mkdn_noop1,
285
286 0, /* entity */
287 0, /* normal_text */
288 "*_", /* emphasis characters */
289 0 /* client data */
290 };
291
+1 -1
--- src/default.css
+++ src/default.css
@@ -1672,11 +1672,11 @@
16721672
}
16731673
div.content div.markdown > ol.footnotes > li {
16741674
margin-bottom: 0.5em;
16751675
}
16761676
div.content div.markdown > ol.footnotes > li > .footnote-backrefs {
1677
- margin-right: 1em;
1677
+ margin-right: 0.5em;
16781678
font-weight: bold;
16791679
}
16801680
div.markdown > ol.footnotes > li > .footnote-backrefs > a:target {
16811681
background: gold;
16821682
}
16831683
--- src/default.css
+++ src/default.css
@@ -1672,11 +1672,11 @@
1672 }
1673 div.content div.markdown > ol.footnotes > li {
1674 margin-bottom: 0.5em;
1675 }
1676 div.content div.markdown > ol.footnotes > li > .footnote-backrefs {
1677 margin-right: 1em;
1678 font-weight: bold;
1679 }
1680 div.markdown > ol.footnotes > li > .footnote-backrefs > a:target {
1681 background: gold;
1682 }
1683
--- src/default.css
+++ src/default.css
@@ -1672,11 +1672,11 @@
1672 }
1673 div.content div.markdown > ol.footnotes > li {
1674 margin-bottom: 0.5em;
1675 }
1676 div.content div.markdown > ol.footnotes > li > .footnote-backrefs {
1677 margin-right: 0.5em;
1678 font-weight: bold;
1679 }
1680 div.markdown > ol.footnotes > li > .footnote-backrefs > a:target {
1681 background: gold;
1682 }
1683
+94 -46
--- src/markdown.c
+++ src/markdown.c
@@ -1066,18 +1066,17 @@
10661066
char *data,
10671067
size_t offset,
10681068
size_t size
10691069
){
10701070
const int is_img = (offset && data[-1] == '!');
1071
- const int is_inline = (offset && data[-1]=='^');
1072
- const int is_note = !is_img && (is_inline || (size>1 && data[1]=='^'));
10731071
size_t i = 1, txt_e;
10741072
struct Blob *content = 0;
10751073
struct Blob *link = 0;
10761074
struct Blob *title = 0;
10771075
const struct footnote *fn = 0;
10781076
int level, ret;
1077
+ /* ? FIXME: assert( size>0 ); */
10791078
10801079
/* checking whether the correct renderer exists */
10811080
if( (is_img && !rndr->make.image) || (!is_img && !rndr->make.link) ){
10821081
return 0;
10831082
}
@@ -1139,23 +1138,33 @@
11391138
}else{
11401139
/* explicit id - between brackets */
11411140
id_data = data+i+1;
11421141
id_size = id_end-(i+1);
11431142
}
1143
+
11441144
if( get_link_ref(rndr, link, title, id_data, id_size)<0 ){
11451145
goto char_link_cleanup;
11461146
}
1147
+
11471148
i = id_end+1;
11481149
11491150
/* shortcut reference style link */
11501151
}else{
1151
- if( is_note ){
1152
- if( is_inline ){
1153
- //fn = put_footnote(rndr, data+1, txt_e-1);
1154
- }else{
1155
- fn = get_footnote(rndr, data+1, txt_e-1);
1156
- }
1152
+ if( offset && data[-1]=='^' ){
1153
+
1154
+ /* free-standing inline note */
1155
+ struct footnote note = {empty_blob,empty_blob,0,0};
1156
+ note.index = ++(rndr->iNotesCount);
1157
+ note.nUsed = 1;
1158
+ blob_append(&note.text,data+1,txt_e-1);
1159
+ blob_append(&rndr->notes, (char *)&note, sizeof note);
1160
+ fn = (struct footnote*)(blob_buffer(&rndr->notes)
1161
+ + blob_size(&rndr->notes) - sizeof(note));
1162
+ }else if(!is_img && size>2 && data[1]=='^'){
1163
+
1164
+ /* free-standing reference */
1165
+ fn = get_footnote(rndr, data+1, txt_e-1);
11571166
if( !fn ) goto char_link_cleanup;
11581167
}else if( get_link_ref(rndr, link, title, data+1, txt_e-1)<0 ){
11591168
goto char_link_cleanup;
11601169
}
11611170
@@ -1164,18 +1173,20 @@
11641173
}
11651174
11661175
/* building content: img alt is escaped, link content is parsed */
11671176
if( txt_e>1 ){
11681177
if( is_img ) blob_append(content, data+1, txt_e-1);
1169
- else if(is_inline) parse_inline(content, rndr, data+1, txt_e-1);
1178
+ else if(!fn) parse_inline(content, rndr, data+1, txt_e-1);
11701179
}
11711180
11721181
/* calling the relevant rendering function */
11731182
if( is_img ){
11741183
if( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]=='!' ) ob->nUsed--;
11751184
ret = rndr->make.image(ob, link, title, content, rndr->make.opaque);
11761185
}else if(fn){
1186
+ if( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]=='^' ) ob->nUsed--;
1187
+ /* ? FIXME: the above line looks like a hack */
11771188
if(rndr->make.footnote_ref){
11781189
ret = rndr->make.footnote_ref(ob,fn->index,fn->nUsed,rndr->make.opaque);
11791190
}
11801191
}else{
11811192
ret = rndr->make.link(ob, link, title, content, rndr->make.opaque);
@@ -2160,12 +2171,14 @@
21602171
}
21612172
}
21622173
i += beg;
21632174
21642175
/* id part: anything but a newline between brackets */
2165
- if( data[i]!='[' || data[i+1]=='^' ) return 0;
2176
+ if( data[i]!='[' ) return 0;
21662177
i++;
2178
+ if( i>=end || data[i]=='^' ) return 0; /* see is_footnote() */
2179
+
21672180
id_offset = i;
21682181
while( i<end && data[i]!='\n' && data[i]!='\r' && data[i]!=']' ){ i++; }
21692182
if( i>=end || data[i]!=']' ) return 0;
21702183
id_end = i;
21712184
@@ -2190,10 +2203,11 @@
21902203
&& data[i]!='\n'
21912204
&& data[i]!='\r'
21922205
){
21932206
i += 1;
21942207
}
2208
+ /* ? FIXME: if( data[i-1]=='>' && data[link_offset-1]!='<' ) */
21952209
if( data[i-1]=='>' ) link_end = i-1; else link_end = i;
21962210
21972211
/* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */
21982212
while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }
21992213
if( i<end
@@ -2253,66 +2267,100 @@
22532267
22542268
/*********************
22552269
* FOOTNOTE PARSING *
22562270
*********************/
22572271
2258
-/* is_footnote -- returns whether a line is a footnote or not */
2272
+/* is_footnote -- check if data holds a definition of a labeled footnote.
2273
+ * If so then append the corresponding element to `footnotes` array */
22592274
static int is_footnote(
22602275
const char *data, /* input text */
22612276
size_t beg, /* offset of the beginning of the line */
22622277
size_t end, /* offset of the end of the text */
22632278
size_t *last, /* last character of the link */
22642279
struct Blob * footnotes
22652280
){
2266
- size_t i = 0;
2267
- size_t id_offset, id_end;
2268
- size_t note_offset, note_end;
2269
- size_t line_end;
2281
+ size_t i, id_offset, id_end;
22702282
struct footnote fn = { empty_blob, empty_blob, 0, 0 };
2283
+
2284
+ /* failfast if data is too short */
2285
+ if( beg+5>=end ) return 0;
2286
+ i = beg;
22712287
22722288
/* footnote definition must start at the begining of a line */
2273
- if( beg+4>=end ) return 0;
2274
- i += beg;
2289
+ if( data[i]!='[' ) return 0;
2290
+ i++;
2291
+ if( data[i]!='^' ) return 0;
2292
+ id_offset = i++;
22752293
22762294
/* id part: anything but a newline between brackets */
2277
- if( data[i]!='[' || data[i+1]!='^' ) return 0;
2278
- i++;
2279
- id_offset = i;
22802295
while( i<end && data[i]!=']' && data[i]!='\n' && data[i]!='\r' ){ i++; }
22812296
if( i>=end || data[i]!=']' ) return 0;
2282
- id_end = i;
2297
+ id_end = i++;
22832298
2284
- /* spacer: colon (space | tab)* newline? (space | tab)* */
2285
- i++;
2299
+ /* spacer: colon (space | tab)* */
22862300
if( i>=end || data[i]!=':' ) return 0;
22872301
i++;
22882302
while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }
2289
- if( i<end && (data[i]=='\n' || data[i]=='\r') ){
2303
+
2304
+ /* passthrough truncated footnote definition
2305
+ * FIXME: maybe omit it? */
2306
+ if( i>=end ) return 0;
2307
+
2308
+ if( build_ref_id(&fn.id, data+id_offset, id_end-id_offset)<0 ) return 0;
2309
+
2310
+ /* footnote's text may start on the same line */
2311
+ if( data[i]!='\n' && data[i]!='\r' ){
2312
+ const size_t j = i;
2313
+ do i++; while( i<end && data[i]!='\n' && data[i]!='\r' );
2314
+ blob_append(&fn.text, data+j, i-j);
2315
+ if( i<end ){
2316
+ blob_append_char(&fn.text, data[i]);
2317
+ i++;
2318
+ if( i<end && data[i]=='\r' && data[i-1] == '\n' ){
2319
+ blob_append_char(&fn.text, data[i]);
2320
+ i++;
2321
+ }
2322
+ }
2323
+ }else{
22902324
i++;
22912325
if( i<end && data[i]=='\r' && data[i-1] == '\n' ) i++;
22922326
}
2293
- while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }
2294
- if( i>=end ) return 0;
2295
-
2296
- /* note is a single line of text (FIXME: support multiline notes) */
2297
- note_offset = i;
2298
- while( i<end && data[i]!='\r' && data[i]!='\n' ){ i++; }
2299
- note_end = i;
2300
-
2301
- /* computing end-of-line */
2302
- line_end = 0;
2303
- if( i >=end || data[i]=='\r' || data[ i ]=='\n' ) line_end = i;
2304
- if( i+1<end && data[i]=='\n' && data[i+1]=='\r' ) line_end = i+1;
2305
-
2306
- if( !line_end ) return 0; /* garbage after the link */
2307
-
2308
- /* a valid note has been found, filling-in note's text */
2309
- if( last ) *last = line_end;
2310
- if( !footnotes ) return 1;
2311
- if( build_ref_id(&fn.id, data+id_offset, id_end-id_offset)<0 ) return 0;
2312
- blob_append(&fn.text, data+note_offset, note_end-note_offset);
2313
- blob_append(footnotes, (char *)&fn, sizeof fn);
2327
+ if( i<end ){
2328
+
2329
+ /* compute the indentation from the 2nd line */
2330
+ size_t indent = i;
2331
+ const char *spaces = data+i;
2332
+ while( i<end && data[i]==' ' ){ i++; }
2333
+ if( i>=end ) goto footnote_finish;
2334
+ indent = i - indent;
2335
+ i -= indent;
2336
+ if( indent<2 ) goto footnote_finish;
2337
+
2338
+ /* process the 2nd and the following lines */
2339
+ while( i+indent<end && memcmp(data+i,spaces,indent)==0 ){
2340
+ size_t j;
2341
+ i += indent;
2342
+ j = i;
2343
+ while( i<end && data[i]!='\n' && data[i]!='\r' ) i++;
2344
+ blob_append(&fn.text, data+j, i-j);
2345
+ if( i>=end ) break;
2346
+ blob_append_char(&fn.text, data[i]);
2347
+ i++;
2348
+ if( i<end && data[i]=='\r' && data[i-1] == '\n' ){
2349
+ blob_append_char(&fn.text, data[i]);
2350
+ i++;
2351
+ }
2352
+ }
2353
+ }
2354
+footnote_finish:
2355
+ if( !blob_size(&fn.text) ){
2356
+ blob_reset(&fn.id);
2357
+ return 0;
2358
+ }
2359
+ /* a valid note has been found */
2360
+ if( last ) *last = i;
2361
+ if( footnotes ) blob_append(footnotes, (char *)&fn, sizeof fn);
23142362
return 1;
23152363
}
23162364
23172365
/**********************
23182366
* EXPORTED FUNCTIONS *
@@ -2434,13 +2482,13 @@
24342482
blob_reset(&lr[i].title);
24352483
}
24362484
blob_reset(&rndr.refs);
24372485
end = COUNT_FOOTNOTES(&rndr.notes);
24382486
for(i=0; i<end; i++){
2439
- blob_reset(&fn[i].id);
2487
+ if(blob_size(&fn[i].id)) blob_reset(&fn[i].id);
24402488
blob_reset(&fn[i].text);
24412489
}
24422490
blob_reset(&rndr.notes);
24432491
for(i=0; i<rndr.nBlobCache; i++){
24442492
fossil_free(rndr.aBlobCache[i]);
24452493
}
24462494
}
24472495
--- src/markdown.c
+++ src/markdown.c
@@ -1066,18 +1066,17 @@
1066 char *data,
1067 size_t offset,
1068 size_t size
1069 ){
1070 const int is_img = (offset && data[-1] == '!');
1071 const int is_inline = (offset && data[-1]=='^');
1072 const int is_note = !is_img && (is_inline || (size>1 && data[1]=='^'));
1073 size_t i = 1, txt_e;
1074 struct Blob *content = 0;
1075 struct Blob *link = 0;
1076 struct Blob *title = 0;
1077 const struct footnote *fn = 0;
1078 int level, ret;
 
1079
1080 /* checking whether the correct renderer exists */
1081 if( (is_img && !rndr->make.image) || (!is_img && !rndr->make.link) ){
1082 return 0;
1083 }
@@ -1139,23 +1138,33 @@
1139 }else{
1140 /* explicit id - between brackets */
1141 id_data = data+i+1;
1142 id_size = id_end-(i+1);
1143 }
 
1144 if( get_link_ref(rndr, link, title, id_data, id_size)<0 ){
1145 goto char_link_cleanup;
1146 }
 
1147 i = id_end+1;
1148
1149 /* shortcut reference style link */
1150 }else{
1151 if( is_note ){
1152 if( is_inline ){
1153 //fn = put_footnote(rndr, data+1, txt_e-1);
1154 }else{
1155 fn = get_footnote(rndr, data+1, txt_e-1);
1156 }
 
 
 
 
 
 
 
 
1157 if( !fn ) goto char_link_cleanup;
1158 }else if( get_link_ref(rndr, link, title, data+1, txt_e-1)<0 ){
1159 goto char_link_cleanup;
1160 }
1161
@@ -1164,18 +1173,20 @@
1164 }
1165
1166 /* building content: img alt is escaped, link content is parsed */
1167 if( txt_e>1 ){
1168 if( is_img ) blob_append(content, data+1, txt_e-1);
1169 else if(is_inline) parse_inline(content, rndr, data+1, txt_e-1);
1170 }
1171
1172 /* calling the relevant rendering function */
1173 if( is_img ){
1174 if( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]=='!' ) ob->nUsed--;
1175 ret = rndr->make.image(ob, link, title, content, rndr->make.opaque);
1176 }else if(fn){
 
 
1177 if(rndr->make.footnote_ref){
1178 ret = rndr->make.footnote_ref(ob,fn->index,fn->nUsed,rndr->make.opaque);
1179 }
1180 }else{
1181 ret = rndr->make.link(ob, link, title, content, rndr->make.opaque);
@@ -2160,12 +2171,14 @@
2160 }
2161 }
2162 i += beg;
2163
2164 /* id part: anything but a newline between brackets */
2165 if( data[i]!='[' || data[i+1]=='^' ) return 0;
2166 i++;
 
 
2167 id_offset = i;
2168 while( i<end && data[i]!='\n' && data[i]!='\r' && data[i]!=']' ){ i++; }
2169 if( i>=end || data[i]!=']' ) return 0;
2170 id_end = i;
2171
@@ -2190,10 +2203,11 @@
2190 && data[i]!='\n'
2191 && data[i]!='\r'
2192 ){
2193 i += 1;
2194 }
 
2195 if( data[i-1]=='>' ) link_end = i-1; else link_end = i;
2196
2197 /* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */
2198 while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }
2199 if( i<end
@@ -2253,66 +2267,100 @@
2253
2254 /*********************
2255 * FOOTNOTE PARSING *
2256 *********************/
2257
2258 /* is_footnote -- returns whether a line is a footnote or not */
 
2259 static int is_footnote(
2260 const char *data, /* input text */
2261 size_t beg, /* offset of the beginning of the line */
2262 size_t end, /* offset of the end of the text */
2263 size_t *last, /* last character of the link */
2264 struct Blob * footnotes
2265 ){
2266 size_t i = 0;
2267 size_t id_offset, id_end;
2268 size_t note_offset, note_end;
2269 size_t line_end;
2270 struct footnote fn = { empty_blob, empty_blob, 0, 0 };
 
 
 
 
2271
2272 /* footnote definition must start at the begining of a line */
2273 if( beg+4>=end ) return 0;
2274 i += beg;
 
 
2275
2276 /* id part: anything but a newline between brackets */
2277 if( data[i]!='[' || data[i+1]!='^' ) return 0;
2278 i++;
2279 id_offset = i;
2280 while( i<end && data[i]!=']' && data[i]!='\n' && data[i]!='\r' ){ i++; }
2281 if( i>=end || data[i]!=']' ) return 0;
2282 id_end = i;
2283
2284 /* spacer: colon (space | tab)* newline? (space | tab)* */
2285 i++;
2286 if( i>=end || data[i]!=':' ) return 0;
2287 i++;
2288 while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }
2289 if( i<end && (data[i]=='\n' || data[i]=='\r') ){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2290 i++;
2291 if( i<end && data[i]=='\r' && data[i-1] == '\n' ) i++;
2292 }
2293 while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }
2294 if( i>=end ) return 0;
2295
2296 /* note is a single line of text (FIXME: support multiline notes) */
2297 note_offset = i;
2298 while( i<end && data[i]!='\r' && data[i]!='\n' ){ i++; }
2299 note_end = i;
2300
2301 /* computing end-of-line */
2302 line_end = 0;
2303 if( i >=end || data[i]=='\r' || data[ i ]=='\n' ) line_end = i;
2304 if( i+1<end && data[i]=='\n' && data[i+1]=='\r' ) line_end = i+1;
2305
2306 if( !line_end ) return 0; /* garbage after the link */
2307
2308 /* a valid note has been found, filling-in note's text */
2309 if( last ) *last = line_end;
2310 if( !footnotes ) return 1;
2311 if( build_ref_id(&fn.id, data+id_offset, id_end-id_offset)<0 ) return 0;
2312 blob_append(&fn.text, data+note_offset, note_end-note_offset);
2313 blob_append(footnotes, (char *)&fn, sizeof fn);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2314 return 1;
2315 }
2316
2317 /**********************
2318 * EXPORTED FUNCTIONS *
@@ -2434,13 +2482,13 @@
2434 blob_reset(&lr[i].title);
2435 }
2436 blob_reset(&rndr.refs);
2437 end = COUNT_FOOTNOTES(&rndr.notes);
2438 for(i=0; i<end; i++){
2439 blob_reset(&fn[i].id);
2440 blob_reset(&fn[i].text);
2441 }
2442 blob_reset(&rndr.notes);
2443 for(i=0; i<rndr.nBlobCache; i++){
2444 fossil_free(rndr.aBlobCache[i]);
2445 }
2446 }
2447
--- src/markdown.c
+++ src/markdown.c
@@ -1066,18 +1066,17 @@
1066 char *data,
1067 size_t offset,
1068 size_t size
1069 ){
1070 const int is_img = (offset && data[-1] == '!');
 
 
1071 size_t i = 1, txt_e;
1072 struct Blob *content = 0;
1073 struct Blob *link = 0;
1074 struct Blob *title = 0;
1075 const struct footnote *fn = 0;
1076 int level, ret;
1077 /* ? FIXME: assert( size>0 ); */
1078
1079 /* checking whether the correct renderer exists */
1080 if( (is_img && !rndr->make.image) || (!is_img && !rndr->make.link) ){
1081 return 0;
1082 }
@@ -1139,23 +1138,33 @@
1138 }else{
1139 /* explicit id - between brackets */
1140 id_data = data+i+1;
1141 id_size = id_end-(i+1);
1142 }
1143
1144 if( get_link_ref(rndr, link, title, id_data, id_size)<0 ){
1145 goto char_link_cleanup;
1146 }
1147
1148 i = id_end+1;
1149
1150 /* shortcut reference style link */
1151 }else{
1152 if( offset && data[-1]=='^' ){
1153
1154 /* free-standing inline note */
1155 struct footnote note = {empty_blob,empty_blob,0,0};
1156 note.index = ++(rndr->iNotesCount);
1157 note.nUsed = 1;
1158 blob_append(&note.text,data+1,txt_e-1);
1159 blob_append(&rndr->notes, (char *)&note, sizeof note);
1160 fn = (struct footnote*)(blob_buffer(&rndr->notes)
1161 + blob_size(&rndr->notes) - sizeof(note));
1162 }else if(!is_img && size>2 && data[1]=='^'){
1163
1164 /* free-standing reference */
1165 fn = get_footnote(rndr, data+1, txt_e-1);
1166 if( !fn ) goto char_link_cleanup;
1167 }else if( get_link_ref(rndr, link, title, data+1, txt_e-1)<0 ){
1168 goto char_link_cleanup;
1169 }
1170
@@ -1164,18 +1173,20 @@
1173 }
1174
1175 /* building content: img alt is escaped, link content is parsed */
1176 if( txt_e>1 ){
1177 if( is_img ) blob_append(content, data+1, txt_e-1);
1178 else if(!fn) parse_inline(content, rndr, data+1, txt_e-1);
1179 }
1180
1181 /* calling the relevant rendering function */
1182 if( is_img ){
1183 if( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]=='!' ) ob->nUsed--;
1184 ret = rndr->make.image(ob, link, title, content, rndr->make.opaque);
1185 }else if(fn){
1186 if( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]=='^' ) ob->nUsed--;
1187 /* ? FIXME: the above line looks like a hack */
1188 if(rndr->make.footnote_ref){
1189 ret = rndr->make.footnote_ref(ob,fn->index,fn->nUsed,rndr->make.opaque);
1190 }
1191 }else{
1192 ret = rndr->make.link(ob, link, title, content, rndr->make.opaque);
@@ -2160,12 +2171,14 @@
2171 }
2172 }
2173 i += beg;
2174
2175 /* id part: anything but a newline between brackets */
2176 if( data[i]!='[' ) return 0;
2177 i++;
2178 if( i>=end || data[i]=='^' ) return 0; /* see is_footnote() */
2179
2180 id_offset = i;
2181 while( i<end && data[i]!='\n' && data[i]!='\r' && data[i]!=']' ){ i++; }
2182 if( i>=end || data[i]!=']' ) return 0;
2183 id_end = i;
2184
@@ -2190,10 +2203,11 @@
2203 && data[i]!='\n'
2204 && data[i]!='\r'
2205 ){
2206 i += 1;
2207 }
2208 /* ? FIXME: if( data[i-1]=='>' && data[link_offset-1]!='<' ) */
2209 if( data[i-1]=='>' ) link_end = i-1; else link_end = i;
2210
2211 /* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */
2212 while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }
2213 if( i<end
@@ -2253,66 +2267,100 @@
2267
2268 /*********************
2269 * FOOTNOTE PARSING *
2270 *********************/
2271
2272 /* is_footnote -- check if data holds a definition of a labeled footnote.
2273 * If so then append the corresponding element to `footnotes` array */
2274 static int is_footnote(
2275 const char *data, /* input text */
2276 size_t beg, /* offset of the beginning of the line */
2277 size_t end, /* offset of the end of the text */
2278 size_t *last, /* last character of the link */
2279 struct Blob * footnotes
2280 ){
2281 size_t i, id_offset, id_end;
 
 
 
2282 struct footnote fn = { empty_blob, empty_blob, 0, 0 };
2283
2284 /* failfast if data is too short */
2285 if( beg+5>=end ) return 0;
2286 i = beg;
2287
2288 /* footnote definition must start at the begining of a line */
2289 if( data[i]!='[' ) return 0;
2290 i++;
2291 if( data[i]!='^' ) return 0;
2292 id_offset = i++;
2293
2294 /* id part: anything but a newline between brackets */
 
 
 
2295 while( i<end && data[i]!=']' && data[i]!='\n' && data[i]!='\r' ){ i++; }
2296 if( i>=end || data[i]!=']' ) return 0;
2297 id_end = i++;
2298
2299 /* spacer: colon (space | tab)* */
 
2300 if( i>=end || data[i]!=':' ) return 0;
2301 i++;
2302 while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }
2303
2304 /* passthrough truncated footnote definition
2305 * FIXME: maybe omit it? */
2306 if( i>=end ) return 0;
2307
2308 if( build_ref_id(&fn.id, data+id_offset, id_end-id_offset)<0 ) return 0;
2309
2310 /* footnote's text may start on the same line */
2311 if( data[i]!='\n' && data[i]!='\r' ){
2312 const size_t j = i;
2313 do i++; while( i<end && data[i]!='\n' && data[i]!='\r' );
2314 blob_append(&fn.text, data+j, i-j);
2315 if( i<end ){
2316 blob_append_char(&fn.text, data[i]);
2317 i++;
2318 if( i<end && data[i]=='\r' && data[i-1] == '\n' ){
2319 blob_append_char(&fn.text, data[i]);
2320 i++;
2321 }
2322 }
2323 }else{
2324 i++;
2325 if( i<end && data[i]=='\r' && data[i-1] == '\n' ) i++;
2326 }
2327 if( i<end ){
2328
2329 /* compute the indentation from the 2nd line */
2330 size_t indent = i;
2331 const char *spaces = data+i;
2332 while( i<end && data[i]==' ' ){ i++; }
2333 if( i>=end ) goto footnote_finish;
2334 indent = i - indent;
2335 i -= indent;
2336 if( indent<2 ) goto footnote_finish;
2337
2338 /* process the 2nd and the following lines */
2339 while( i+indent<end && memcmp(data+i,spaces,indent)==0 ){
2340 size_t j;
2341 i += indent;
2342 j = i;
2343 while( i<end && data[i]!='\n' && data[i]!='\r' ) i++;
2344 blob_append(&fn.text, data+j, i-j);
2345 if( i>=end ) break;
2346 blob_append_char(&fn.text, data[i]);
2347 i++;
2348 if( i<end && data[i]=='\r' && data[i-1] == '\n' ){
2349 blob_append_char(&fn.text, data[i]);
2350 i++;
2351 }
2352 }
2353 }
2354 footnote_finish:
2355 if( !blob_size(&fn.text) ){
2356 blob_reset(&fn.id);
2357 return 0;
2358 }
2359 /* a valid note has been found */
2360 if( last ) *last = i;
2361 if( footnotes ) blob_append(footnotes, (char *)&fn, sizeof fn);
2362 return 1;
2363 }
2364
2365 /**********************
2366 * EXPORTED FUNCTIONS *
@@ -2434,13 +2482,13 @@
2482 blob_reset(&lr[i].title);
2483 }
2484 blob_reset(&rndr.refs);
2485 end = COUNT_FOOTNOTES(&rndr.notes);
2486 for(i=0; i<end; i++){
2487 if(blob_size(&fn[i].id)) blob_reset(&fn[i].id);
2488 blob_reset(&fn[i].text);
2489 }
2490 blob_reset(&rndr.notes);
2491 for(i=0; i<rndr.nBlobCache; i++){
2492 fossil_free(rndr.aBlobCache[i]);
2493 }
2494 }
2495
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -379,11 +379,11 @@
379379
" href='#noteref-%s-%s'>%s</a>",
380380
pos,l.c, pos,l.c, l.c);
381381
}
382382
if( i < nUsed ) BLOB_APPEND_LITERAL(ob," &hellip;");
383383
}
384
- BLOB_APPEND_LITERAL(ob,"</sup>\n\t");
384
+ BLOB_APPEND_LITERAL(ob,"</sup>\n");
385385
BLOB_APPEND_BLOB(ob, text);
386386
BLOB_APPEND_LITERAL(ob, "\n</li>\n");
387387
}
388388
static void html_footnotes(
389389
struct Blob *ob, const struct Blob *items, void *opaque
390390
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -379,11 +379,11 @@
379 " href='#noteref-%s-%s'>%s</a>",
380 pos,l.c, pos,l.c, l.c);
381 }
382 if( i < nUsed ) BLOB_APPEND_LITERAL(ob," &hellip;");
383 }
384 BLOB_APPEND_LITERAL(ob,"</sup>\n\t");
385 BLOB_APPEND_BLOB(ob, text);
386 BLOB_APPEND_LITERAL(ob, "\n</li>\n");
387 }
388 static void html_footnotes(
389 struct Blob *ob, const struct Blob *items, void *opaque
390
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -379,11 +379,11 @@
379 " href='#noteref-%s-%s'>%s</a>",
380 pos,l.c, pos,l.c, l.c);
381 }
382 if( i < nUsed ) BLOB_APPEND_LITERAL(ob," &hellip;");
383 }
384 BLOB_APPEND_LITERAL(ob,"</sup>\n");
385 BLOB_APPEND_BLOB(ob, text);
386 BLOB_APPEND_LITERAL(ob, "\n</li>\n");
387 }
388 static void html_footnotes(
389 struct Blob *ob, const struct Blob *items, void *opaque
390

Keyboard Shortcuts

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