Fossil SCM

Switch to <tt>(^...)</tt> for inline footnotes. Implement span-specific footnotes. Add [/doc/markdown-footnotes/src/markdown.md#ftnts|documentation].

george 2022-02-04 00:37 markdown-footnotes
Commit cae7a5d1cab9b6101ae7230468f7281856f933abc530fdff50643752f6471299
+1 -1
--- src/backlink.c
+++ src/backlink.c
@@ -279,11 +279,11 @@
279279
/* image */ (int(*)(Blob*,Blob*,Blob*,Blob*,void*))mkdn_noop1,
280280
/* linebreak */ (int(*)(Blob*,void*))mkdn_noop1,
281281
/* link */ backlink_md_link,
282282
/* r_html_tag */ (int(*)(Blob*,Blob*,void*))mkdn_noop1,
283283
/* tri_emphas */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1,
284
- /* footnoteref*/ (int(*)(Blob*,int,int,void*))mkdn_noop1,
284
+ /* footnoteref*/ (int(*)(Blob*,const Blob*,int,int,void*))mkdn_noop1,
285285
286286
0, /* entity */
287287
0, /* normal_text */
288288
"*_", /* emphasis characters */
289289
0 /* client data */
290290
--- src/backlink.c
+++ src/backlink.c
@@ -279,11 +279,11 @@
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
--- src/backlink.c
+++ src/backlink.c
@@ -279,11 +279,11 @@
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*,const Blob*,int,int,void*))mkdn_noop1,
285
286 0, /* entity */
287 0, /* normal_text */
288 "*_", /* emphasis characters */
289 0 /* client data */
290
--- src/default.css
+++ src/default.css
@@ -1680,10 +1680,16 @@
16801680
div.markdown > ol.footnotes > li > .footnote-backrefs > a:target {
16811681
background: gold;
16821682
}
16831683
div.markdown a.noteref:target > sup {
16841684
background: gold;
1685
+}
1686
+div.markdown span.notescope:target {
1687
+ border-bottom: 2px solid gold;
1688
+}
1689
+div.markdown span.notescope:target > a.noteref > sup {
1690
+ background: gold;
16851691
}
16861692
16871693
/* Objects in the "desktoponly" class are invisible on mobile */
16881694
@media screen and (max-width: 600px) {
16891695
.desktoponly {
16901696
--- src/default.css
+++ src/default.css
@@ -1680,10 +1680,16 @@
1680 div.markdown > ol.footnotes > li > .footnote-backrefs > a:target {
1681 background: gold;
1682 }
1683 div.markdown a.noteref:target > sup {
1684 background: gold;
 
 
 
 
 
 
1685 }
1686
1687 /* Objects in the "desktoponly" class are invisible on mobile */
1688 @media screen and (max-width: 600px) {
1689 .desktoponly {
1690
--- src/default.css
+++ src/default.css
@@ -1680,10 +1680,16 @@
1680 div.markdown > ol.footnotes > li > .footnote-backrefs > a:target {
1681 background: gold;
1682 }
1683 div.markdown a.noteref:target > sup {
1684 background: gold;
1685 }
1686 div.markdown span.notescope:target {
1687 border-bottom: 2px solid gold;
1688 }
1689 div.markdown span.notescope:target > a.noteref > sup {
1690 background: gold;
1691 }
1692
1693 /* Objects in the "desktoponly" class are invisible on mobile */
1694 @media screen and (max-width: 600px) {
1695 .desktoponly {
1696
+113 -42
--- src/markdown.c
+++ src/markdown.c
@@ -82,11 +82,12 @@
8282
int (*link)(struct Blob *ob, struct Blob *link, struct Blob *title,
8383
struct Blob *content, void *opaque);
8484
int (*raw_html_tag)(struct Blob *ob, struct Blob *tag, void *opaque);
8585
int (*triple_emphasis)(struct Blob *ob, struct Blob *text,
8686
char c, void *opaque);
87
- int (*footnote_ref)(struct Blob *ob, int index, int locus, void *opaque);
87
+ int (*footnote_ref)(struct Blob *ob, const struct Blob *span,
88
+ int index, int locus, void *opaque);
8889
8990
/* low level callbacks - NULL copies input directly into the output */
9091
void (*entity)(struct Blob *ob, struct Blob *entity, void *opaque);
9192
void (*normal_text)(struct Blob *ob, struct Blob *text, void *opaque);
9293
@@ -1057,10 +1058,70 @@
10571058
assert( fn->nUsed > 0 );
10581059
cleanup:
10591060
release_work_buffer( rndr, id );
10601061
return fn;
10611062
}
1063
+/* Adds unlabeled footnote to the rndr.
1064
+ * If text is blank then returns 0,
1065
+ * otherwise returns the address of the added footnote. */
1066
+static inline const struct footnote* add_inline_footnote(
1067
+ struct render *rndr,
1068
+ const char *text,
1069
+ size_t size
1070
+){
1071
+ struct footnote fn = { empty_blob, empty_blob, 0, 0 };
1072
+ while(size && (*text==' ' || *text=='\t')){ text++; size--; }
1073
+ if(!size) return 0;
1074
+ fn.index = ++(rndr->iNotesCount);
1075
+ fn.nUsed = 1;
1076
+ assert( fn.index > 0 );
1077
+ blob_append(&fn.text, text, size);
1078
+ blob_append(&rndr->notes, (char *)&fn, sizeof fn);
1079
+ return (struct footnote*)( blob_buffer(&rndr->notes)
1080
+ +( blob_size(&rndr->notes)-sizeof fn ));
1081
+}
1082
+
1083
+/* Return the offset of the matching closing bracket or 0 if not found.
1084
+ * begin[0] must be either '[' or '(' */
1085
+static inline size_t matching_bracket_offset(
1086
+ const char* begin,
1087
+ const char* end
1088
+){
1089
+ const char *i;
1090
+ int level;
1091
+ const char bra = *begin;
1092
+ const char ket = bra=='[' ? ']' : ')';
1093
+ assert( bra=='[' || bra=='(' ); /* FIXME: only when debugging */
1094
+ for(i=begin+1,level=1; i!=end; i++){
1095
+ if( *i=='\n' ) /* do nothing */;
1096
+ else if( i[-1]=='\\' ) continue; /* ? FIXME: what if \\( ? */
1097
+ else if( *i==bra ) level++;
1098
+ else if( *i==ket ){
1099
+ if( --level<=0 ) return i-begin;
1100
+ }
1101
+ }
1102
+ return 0;
1103
+}
1104
+
1105
+/* char_footnote -- '(': parsing a standalone inline footnote */
1106
+static size_t char_footnote(
1107
+ struct Blob *ob,
1108
+ struct render *rndr,
1109
+ char *data,
1110
+ size_t offset,
1111
+ size_t size
1112
+){
1113
+ size_t end;
1114
+ const struct footnote* fn;
1115
+
1116
+ if( size<4 || data[1]!='^' || !rndr->make.footnote_ref ) return 0;
1117
+ end = matching_bracket_offset(data, data+size);
1118
+ if( !end ) return 0;
1119
+ fn = add_inline_footnote(rndr, data+2, end-2);
1120
+ if(fn) rndr->make.footnote_ref(ob,0,fn->index,1,rndr->make.opaque);
1121
+ return end+1;
1122
+}
10621123
10631124
/* char_link -- '[': parsing a link or an image */
10641125
static size_t char_link(
10651126
struct Blob *ob,
10661127
struct render *rndr,
@@ -1104,92 +1165,101 @@
11041165
title = new_work_buffer(rndr);
11051166
content = new_work_buffer(rndr);
11061167
link = new_work_buffer(rndr);
11071168
ret = 0; /* error if we don't get to the callback */
11081169
1109
- /* inline style link */
1170
+ /* inline style link or span-bounded inline footnote */
11101171
if( i<size && data[i]=='(' ){
1111
- size_t span_end = i;
1112
- while( span_end<size
1113
- && !(data[span_end]==')' && (span_end==i || data[span_end-1]!='\\'))
1114
- ){
1115
- span_end++;
1116
- }
1117
-
1118
- if( span_end>=size
1119
- || get_link_inline(link, title, data+i+1, span_end-(i+1))<0
1120
- ){
1121
- goto char_link_cleanup;
1122
- }
1123
-
1124
- i = span_end+1;
1125
-
1126
- /* reference style link */
1172
+
1173
+ /* inline footnote */
1174
+ if( i+2<size && data[i+1]=='^' ){
1175
+
1176
+ const size_t k = matching_bracket_offset(data+i, data+size);
1177
+ if( !k ) goto char_link_cleanup;
1178
+ fn = add_inline_footnote(rndr, data+(i+2), k-2);
1179
+ i += k+1;
1180
+ }else{
1181
+ size_t span_end = i;
1182
+ while( span_end<size
1183
+ && !(data[span_end]==')' && (span_end==i || data[span_end-1]!='\\'))
1184
+ ){
1185
+ span_end++;
1186
+ }
1187
+
1188
+ if( span_end>=size
1189
+ || get_link_inline(link, title, data+i+1, span_end-(i+1))<0
1190
+ ){
1191
+ goto char_link_cleanup;
1192
+ }
1193
+
1194
+ i = span_end+1;
1195
+ }
1196
+
1197
+ /* reference style link or span-bounded footnote reference */
11271198
}else if( i<size && data[i]=='[' ){
11281199
char *id_data;
11291200
size_t id_size, id_end = i;
1201
+ int bFootnote;
11301202
11311203
while( id_end<size && data[id_end]!=']' ){ id_end++; }
11321204
11331205
if( id_end>=size ) goto char_link_cleanup;
1206
+ bFootnote = data[i+1]=='^';
11341207
1135
- if( i+1==id_end ){
1208
+ if( i+1==id_end || (bFootnote && i+2==id_end) ){
11361209
/* implicit id - use the contents */
11371210
id_data = data+1;
11381211
id_size = txt_e-1;
11391212
}else{
11401213
/* explicit id - between brackets */
11411214
id_data = data+i+1;
11421215
id_size = id_end-(i+1);
1216
+ if( bFootnote ){
1217
+ id_data++;
1218
+ id_size--;
1219
+ }
11431220
}
11441221
1145
- if( get_link_ref(rndr, link, title, id_data, id_size)<0 ){
1222
+ if( bFootnote ){
1223
+ fn = get_footnote(rndr, id_data, id_size);
1224
+ if( !fn ) goto char_link_cleanup;
1225
+ }else if( get_link_ref(rndr, link, title, id_data, id_size)<0 ){
11461226
goto char_link_cleanup;
11471227
}
11481228
11491229
i = id_end+1;
11501230
1151
- /* shortcut reference style link */
1231
+ /* shortcut reference style link or free-standing footnote refernece */
11521232
}else{
1153
- if( offset && data[-1]=='^' ){
1154
-
1155
- /* free-standing inline note */
1156
- struct footnote note = {empty_blob,empty_blob,0,0};
1157
- note.index = ++(rndr->iNotesCount);
1158
- note.nUsed = 1;
1159
- blob_append(&note.text,data+1,txt_e-1);
1160
- blob_append(&rndr->notes, (char *)&note, sizeof note);
1161
- fn = (struct footnote*)(blob_buffer(&rndr->notes)
1162
- + blob_size(&rndr->notes) - sizeof(note));
1163
- }else if(!is_img && size>2 && data[1]=='^'){
1164
-
1165
- /* free-standing reference */
1166
- fn = get_footnote(rndr, data+1, txt_e-1);
1233
+ if(!is_img && size>3 && data[1]=='^'){
1234
+ /* free-standing footnote reference */
1235
+ fn = get_footnote(rndr, data+2, txt_e-2);
11671236
if( !fn ) goto char_link_cleanup;
1237
+ release_work_buffer(rndr, content);
1238
+ content = 0;
11681239
}else if( get_link_ref(rndr, link, title, data+1, txt_e-1)<0 ){
11691240
goto char_link_cleanup;
11701241
}
11711242
1172
- /* rewinding the whitespace */
1243
+ /* rewinding a closing square bracket */
11731244
i = txt_e+1;
11741245
}
11751246
11761247
/* building content: img alt is escaped, link content is parsed */
1177
- if( txt_e>1 ){
1248
+ if( txt_e>1 && content ){
11781249
if( is_img ) blob_append(content, data+1, txt_e-1);
1179
- else if(!fn) parse_inline(content, rndr, data+1, txt_e-1);
1250
+ else parse_inline(content, rndr, data+1, txt_e-1);
11801251
}
11811252
11821253
/* calling the relevant rendering function */
11831254
if( is_img ){
11841255
if( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]=='!' ) ob->nUsed--;
11851256
ret = rndr->make.image(ob, link, title, content, rndr->make.opaque);
11861257
}else if(fn){
1187
- if( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]=='^' ) ob->nUsed--;
1188
- /* ? FIXME: the above line looks like a hack */
11891258
if(rndr->make.footnote_ref){
1190
- ret = rndr->make.footnote_ref(ob,fn->index,fn->nUsed,rndr->make.opaque);
1259
+ ret = rndr->make.footnote_ref(ob, content, fn->index, fn->nUsed,
1260
+ rndr->make.opaque);
11911261
}
11921262
}else{
11931263
ret = rndr->make.link(ob, link, title, content, rndr->make.opaque);
11941264
}
11951265
@@ -2288,11 +2358,11 @@
22882358
22892359
/* footnote definition must start at the begining of a line */
22902360
if( data[i]!='[' ) return 0;
22912361
i++;
22922362
if( data[i]!='^' ) return 0;
2293
- id_offset = i++;
2363
+ id_offset = ++i;
22942364
22952365
/* id part: anything but a newline between brackets */
22962366
while( i<end && data[i]!=']' && data[i]!='\n' && data[i]!='\r' ){ i++; }
22972367
if( i>=end || data[i]!=']' ) return 0;
22982368
id_end = i++;
@@ -2398,10 +2468,11 @@
23982468
}
23992469
}
24002470
if( rndr.make.codespan ) rndr.active_char['`'] = char_codespan;
24012471
if( rndr.make.linebreak ) rndr.active_char['\n'] = char_linebreak;
24022472
if( rndr.make.image || rndr.make.link ) rndr.active_char['['] = char_link;
2473
+ if( rndr.make.footnote_ref ) rndr.active_char['('] = char_footnote;
24032474
rndr.active_char['<'] = char_langle_tag;
24042475
rndr.active_char['\\'] = char_escape;
24052476
rndr.active_char['&'] = char_entity;
24062477
24072478
/* first pass: iterate over lines looking for references,
24082479
--- src/markdown.c
+++ src/markdown.c
@@ -82,11 +82,12 @@
82 int (*link)(struct Blob *ob, struct Blob *link, struct Blob *title,
83 struct Blob *content, void *opaque);
84 int (*raw_html_tag)(struct Blob *ob, struct Blob *tag, void *opaque);
85 int (*triple_emphasis)(struct Blob *ob, struct Blob *text,
86 char c, void *opaque);
87 int (*footnote_ref)(struct Blob *ob, int index, int locus, void *opaque);
 
88
89 /* low level callbacks - NULL copies input directly into the output */
90 void (*entity)(struct Blob *ob, struct Blob *entity, void *opaque);
91 void (*normal_text)(struct Blob *ob, struct Blob *text, void *opaque);
92
@@ -1057,10 +1058,70 @@
1057 assert( fn->nUsed > 0 );
1058 cleanup:
1059 release_work_buffer( rndr, id );
1060 return fn;
1061 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1062
1063 /* char_link -- '[': parsing a link or an image */
1064 static size_t char_link(
1065 struct Blob *ob,
1066 struct render *rndr,
@@ -1104,92 +1165,101 @@
1104 title = new_work_buffer(rndr);
1105 content = new_work_buffer(rndr);
1106 link = new_work_buffer(rndr);
1107 ret = 0; /* error if we don't get to the callback */
1108
1109 /* inline style link */
1110 if( i<size && data[i]=='(' ){
1111 size_t span_end = i;
1112 while( span_end<size
1113 && !(data[span_end]==')' && (span_end==i || data[span_end-1]!='\\'))
1114 ){
1115 span_end++;
1116 }
1117
1118 if( span_end>=size
1119 || get_link_inline(link, title, data+i+1, span_end-(i+1))<0
1120 ){
1121 goto char_link_cleanup;
1122 }
1123
1124 i = span_end+1;
1125
1126 /* reference style link */
 
 
 
 
 
 
 
 
 
 
1127 }else if( i<size && data[i]=='[' ){
1128 char *id_data;
1129 size_t id_size, id_end = i;
 
1130
1131 while( id_end<size && data[id_end]!=']' ){ id_end++; }
1132
1133 if( id_end>=size ) goto char_link_cleanup;
 
1134
1135 if( i+1==id_end ){
1136 /* implicit id - use the contents */
1137 id_data = data+1;
1138 id_size = txt_e-1;
1139 }else{
1140 /* explicit id - between brackets */
1141 id_data = data+i+1;
1142 id_size = id_end-(i+1);
 
 
 
 
1143 }
1144
1145 if( get_link_ref(rndr, link, title, id_data, id_size)<0 ){
 
 
 
1146 goto char_link_cleanup;
1147 }
1148
1149 i = id_end+1;
1150
1151 /* shortcut reference style link */
1152 }else{
1153 if( offset && data[-1]=='^' ){
1154
1155 /* free-standing inline note */
1156 struct footnote note = {empty_blob,empty_blob,0,0};
1157 note.index = ++(rndr->iNotesCount);
1158 note.nUsed = 1;
1159 blob_append(&note.text,data+1,txt_e-1);
1160 blob_append(&rndr->notes, (char *)&note, sizeof note);
1161 fn = (struct footnote*)(blob_buffer(&rndr->notes)
1162 + blob_size(&rndr->notes) - sizeof(note));
1163 }else if(!is_img && size>2 && data[1]=='^'){
1164
1165 /* free-standing reference */
1166 fn = get_footnote(rndr, data+1, txt_e-1);
1167 if( !fn ) goto char_link_cleanup;
 
 
1168 }else if( get_link_ref(rndr, link, title, data+1, txt_e-1)<0 ){
1169 goto char_link_cleanup;
1170 }
1171
1172 /* rewinding the whitespace */
1173 i = txt_e+1;
1174 }
1175
1176 /* building content: img alt is escaped, link content is parsed */
1177 if( txt_e>1 ){
1178 if( is_img ) blob_append(content, data+1, txt_e-1);
1179 else if(!fn) parse_inline(content, rndr, data+1, txt_e-1);
1180 }
1181
1182 /* calling the relevant rendering function */
1183 if( is_img ){
1184 if( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]=='!' ) ob->nUsed--;
1185 ret = rndr->make.image(ob, link, title, content, rndr->make.opaque);
1186 }else if(fn){
1187 if( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]=='^' ) ob->nUsed--;
1188 /* ? FIXME: the above line looks like a hack */
1189 if(rndr->make.footnote_ref){
1190 ret = rndr->make.footnote_ref(ob,fn->index,fn->nUsed,rndr->make.opaque);
 
1191 }
1192 }else{
1193 ret = rndr->make.link(ob, link, title, content, rndr->make.opaque);
1194 }
1195
@@ -2288,11 +2358,11 @@
2288
2289 /* footnote definition must start at the begining of a line */
2290 if( data[i]!='[' ) return 0;
2291 i++;
2292 if( data[i]!='^' ) return 0;
2293 id_offset = i++;
2294
2295 /* id part: anything but a newline between brackets */
2296 while( i<end && data[i]!=']' && data[i]!='\n' && data[i]!='\r' ){ i++; }
2297 if( i>=end || data[i]!=']' ) return 0;
2298 id_end = i++;
@@ -2398,10 +2468,11 @@
2398 }
2399 }
2400 if( rndr.make.codespan ) rndr.active_char['`'] = char_codespan;
2401 if( rndr.make.linebreak ) rndr.active_char['\n'] = char_linebreak;
2402 if( rndr.make.image || rndr.make.link ) rndr.active_char['['] = char_link;
 
2403 rndr.active_char['<'] = char_langle_tag;
2404 rndr.active_char['\\'] = char_escape;
2405 rndr.active_char['&'] = char_entity;
2406
2407 /* first pass: iterate over lines looking for references,
2408
--- src/markdown.c
+++ src/markdown.c
@@ -82,11 +82,12 @@
82 int (*link)(struct Blob *ob, struct Blob *link, struct Blob *title,
83 struct Blob *content, void *opaque);
84 int (*raw_html_tag)(struct Blob *ob, struct Blob *tag, void *opaque);
85 int (*triple_emphasis)(struct Blob *ob, struct Blob *text,
86 char c, void *opaque);
87 int (*footnote_ref)(struct Blob *ob, const struct Blob *span,
88 int index, int locus, void *opaque);
89
90 /* low level callbacks - NULL copies input directly into the output */
91 void (*entity)(struct Blob *ob, struct Blob *entity, void *opaque);
92 void (*normal_text)(struct Blob *ob, struct Blob *text, void *opaque);
93
@@ -1057,10 +1058,70 @@
1058 assert( fn->nUsed > 0 );
1059 cleanup:
1060 release_work_buffer( rndr, id );
1061 return fn;
1062 }
1063 /* Adds unlabeled footnote to the rndr.
1064 * If text is blank then returns 0,
1065 * otherwise returns the address of the added footnote. */
1066 static inline const struct footnote* add_inline_footnote(
1067 struct render *rndr,
1068 const char *text,
1069 size_t size
1070 ){
1071 struct footnote fn = { empty_blob, empty_blob, 0, 0 };
1072 while(size && (*text==' ' || *text=='\t')){ text++; size--; }
1073 if(!size) return 0;
1074 fn.index = ++(rndr->iNotesCount);
1075 fn.nUsed = 1;
1076 assert( fn.index > 0 );
1077 blob_append(&fn.text, text, size);
1078 blob_append(&rndr->notes, (char *)&fn, sizeof fn);
1079 return (struct footnote*)( blob_buffer(&rndr->notes)
1080 +( blob_size(&rndr->notes)-sizeof fn ));
1081 }
1082
1083 /* Return the offset of the matching closing bracket or 0 if not found.
1084 * begin[0] must be either '[' or '(' */
1085 static inline size_t matching_bracket_offset(
1086 const char* begin,
1087 const char* end
1088 ){
1089 const char *i;
1090 int level;
1091 const char bra = *begin;
1092 const char ket = bra=='[' ? ']' : ')';
1093 assert( bra=='[' || bra=='(' ); /* FIXME: only when debugging */
1094 for(i=begin+1,level=1; i!=end; i++){
1095 if( *i=='\n' ) /* do nothing */;
1096 else if( i[-1]=='\\' ) continue; /* ? FIXME: what if \\( ? */
1097 else if( *i==bra ) level++;
1098 else if( *i==ket ){
1099 if( --level<=0 ) return i-begin;
1100 }
1101 }
1102 return 0;
1103 }
1104
1105 /* char_footnote -- '(': parsing a standalone inline footnote */
1106 static size_t char_footnote(
1107 struct Blob *ob,
1108 struct render *rndr,
1109 char *data,
1110 size_t offset,
1111 size_t size
1112 ){
1113 size_t end;
1114 const struct footnote* fn;
1115
1116 if( size<4 || data[1]!='^' || !rndr->make.footnote_ref ) return 0;
1117 end = matching_bracket_offset(data, data+size);
1118 if( !end ) return 0;
1119 fn = add_inline_footnote(rndr, data+2, end-2);
1120 if(fn) rndr->make.footnote_ref(ob,0,fn->index,1,rndr->make.opaque);
1121 return end+1;
1122 }
1123
1124 /* char_link -- '[': parsing a link or an image */
1125 static size_t char_link(
1126 struct Blob *ob,
1127 struct render *rndr,
@@ -1104,92 +1165,101 @@
1165 title = new_work_buffer(rndr);
1166 content = new_work_buffer(rndr);
1167 link = new_work_buffer(rndr);
1168 ret = 0; /* error if we don't get to the callback */
1169
1170 /* inline style link or span-bounded inline footnote */
1171 if( i<size && data[i]=='(' ){
1172
1173 /* inline footnote */
1174 if( i+2<size && data[i+1]=='^' ){
1175
1176 const size_t k = matching_bracket_offset(data+i, data+size);
1177 if( !k ) goto char_link_cleanup;
1178 fn = add_inline_footnote(rndr, data+(i+2), k-2);
1179 i += k+1;
1180 }else{
1181 size_t span_end = i;
1182 while( span_end<size
1183 && !(data[span_end]==')' && (span_end==i || data[span_end-1]!='\\'))
1184 ){
1185 span_end++;
1186 }
1187
1188 if( span_end>=size
1189 || get_link_inline(link, title, data+i+1, span_end-(i+1))<0
1190 ){
1191 goto char_link_cleanup;
1192 }
1193
1194 i = span_end+1;
1195 }
1196
1197 /* reference style link or span-bounded footnote reference */
1198 }else if( i<size && data[i]=='[' ){
1199 char *id_data;
1200 size_t id_size, id_end = i;
1201 int bFootnote;
1202
1203 while( id_end<size && data[id_end]!=']' ){ id_end++; }
1204
1205 if( id_end>=size ) goto char_link_cleanup;
1206 bFootnote = data[i+1]=='^';
1207
1208 if( i+1==id_end || (bFootnote && i+2==id_end) ){
1209 /* implicit id - use the contents */
1210 id_data = data+1;
1211 id_size = txt_e-1;
1212 }else{
1213 /* explicit id - between brackets */
1214 id_data = data+i+1;
1215 id_size = id_end-(i+1);
1216 if( bFootnote ){
1217 id_data++;
1218 id_size--;
1219 }
1220 }
1221
1222 if( bFootnote ){
1223 fn = get_footnote(rndr, id_data, id_size);
1224 if( !fn ) goto char_link_cleanup;
1225 }else if( get_link_ref(rndr, link, title, id_data, id_size)<0 ){
1226 goto char_link_cleanup;
1227 }
1228
1229 i = id_end+1;
1230
1231 /* shortcut reference style link or free-standing footnote refernece */
1232 }else{
1233 if(!is_img && size>3 && data[1]=='^'){
1234 /* free-standing footnote reference */
1235 fn = get_footnote(rndr, data+2, txt_e-2);
 
 
 
 
 
 
 
 
 
 
 
1236 if( !fn ) goto char_link_cleanup;
1237 release_work_buffer(rndr, content);
1238 content = 0;
1239 }else if( get_link_ref(rndr, link, title, data+1, txt_e-1)<0 ){
1240 goto char_link_cleanup;
1241 }
1242
1243 /* rewinding a closing square bracket */
1244 i = txt_e+1;
1245 }
1246
1247 /* building content: img alt is escaped, link content is parsed */
1248 if( txt_e>1 && content ){
1249 if( is_img ) blob_append(content, data+1, txt_e-1);
1250 else parse_inline(content, rndr, data+1, txt_e-1);
1251 }
1252
1253 /* calling the relevant rendering function */
1254 if( is_img ){
1255 if( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]=='!' ) ob->nUsed--;
1256 ret = rndr->make.image(ob, link, title, content, rndr->make.opaque);
1257 }else if(fn){
 
 
1258 if(rndr->make.footnote_ref){
1259 ret = rndr->make.footnote_ref(ob, content, fn->index, fn->nUsed,
1260 rndr->make.opaque);
1261 }
1262 }else{
1263 ret = rndr->make.link(ob, link, title, content, rndr->make.opaque);
1264 }
1265
@@ -2288,11 +2358,11 @@
2358
2359 /* footnote definition must start at the begining of a line */
2360 if( data[i]!='[' ) return 0;
2361 i++;
2362 if( data[i]!='^' ) return 0;
2363 id_offset = ++i;
2364
2365 /* id part: anything but a newline between brackets */
2366 while( i<end && data[i]!=']' && data[i]!='\n' && data[i]!='\r' ){ i++; }
2367 if( i>=end || data[i]!=']' ) return 0;
2368 id_end = i++;
@@ -2398,10 +2468,11 @@
2468 }
2469 }
2470 if( rndr.make.codespan ) rndr.active_char['`'] = char_codespan;
2471 if( rndr.make.linebreak ) rndr.active_char['\n'] = char_linebreak;
2472 if( rndr.make.image || rndr.make.link ) rndr.active_char['['] = char_link;
2473 if( rndr.make.footnote_ref ) rndr.active_char['('] = char_footnote;
2474 rndr.active_char['<'] = char_langle_tag;
2475 rndr.active_char['\\'] = char_escape;
2476 rndr.active_char['&'] = char_entity;
2477
2478 /* first pass: iterate over lines looking for references,
2479
--- src/markdown.md
+++ src/markdown.md
@@ -150,10 +150,43 @@
150150
>
151151
~~~ pikchr
152152
oval "Start" fit; arrow; box "Hello, World!" fit; arrow; oval "Done" fit
153153
~~~
154154
155
+<a id="ftnts"></a>
156
+## Footnotes ##
157
+
158
+> Footnotes (or "endnotes") is a Fossil's extention of classical Markdown.
159
+> Fossil's syntax for footnotes is similar to links and
160
+> is distinguished by the use of character **^**
161
+> that *immediately* follows an opening bracket.
162
+
163
+> 1. **\(^** footnote's text **)**
164
+> 2. **\[** fragment of text **]\(^** a comment about that fragment **\)**
165
+> 3. **\[^**&nbsp;label&nbsp;**\]**
166
+> 4. **\[** fragment of text **\]\[^**&nbsp;label&nbsp;**\]**
167
+> 5. **\[** fragment of text **\]\[^\]**
168
+
169
+> With formats 1 and 2 ("inline footnotes") text of a footnote is provided
170
+> in the place where the corresponding numeric mark will be rendered.
171
+> With formats 3, 4, and 5 ("reference footnotes") text of a footnote
172
+> is supplied elsewhere in the document, as shown below.
173
+> Formats 2, 4 and 5 ("span-specific footnotes") mark a specific fragment
174
+> that is being commented in the footnote.
175
+> Format 5 reuses a fragment of text as a label.
176
+> Labels are case-insensitive.
177
+
178
+>
179
+```
180
+[^label]: Footnote definition must start on the first column.
181
+ The second line (if any) must be indented by two or more spaces.
182
+ Definition continues until indentation drops below that of the 2nd line.
183
+```
184
+> Charachter **^** is not part of a label, it is part of the syntax.
185
+> Both a footnote's text and a fragment to which a footnote applies
186
+> are subject to further interpretation as Markdown sources.
187
+
155188
## Miscellaneous ##
156189
157190
> * In-line images are made using **\!\[alt-text\]\(image-URL\)**.
158191
> * Use HTML for advanced formatting such as forms.
159192
> * **\<!--** HTML-style comments **-->** are supported.
160193
--- src/markdown.md
+++ src/markdown.md
@@ -150,10 +150,43 @@
150 >
151 ~~~ pikchr
152 oval "Start" fit; arrow; box "Hello, World!" fit; arrow; oval "Done" fit
153 ~~~
154
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155 ## Miscellaneous ##
156
157 > * In-line images are made using **\!\[alt-text\]\(image-URL\)**.
158 > * Use HTML for advanced formatting such as forms.
159 > * **\<!--** HTML-style comments **-->** are supported.
160
--- src/markdown.md
+++ src/markdown.md
@@ -150,10 +150,43 @@
150 >
151 ~~~ pikchr
152 oval "Start" fit; arrow; box "Hello, World!" fit; arrow; oval "Done" fit
153 ~~~
154
155 <a id="ftnts"></a>
156 ## Footnotes ##
157
158 > Footnotes (or "endnotes") is a Fossil's extention of classical Markdown.
159 > Fossil's syntax for footnotes is similar to links and
160 > is distinguished by the use of character **^**
161 > that *immediately* follows an opening bracket.
162
163 > 1. **\(^** footnote's text **)**
164 > 2. **\[** fragment of text **]\(^** a comment about that fragment **\)**
165 > 3. **\[^**&nbsp;label&nbsp;**\]**
166 > 4. **\[** fragment of text **\]\[^**&nbsp;label&nbsp;**\]**
167 > 5. **\[** fragment of text **\]\[^\]**
168
169 > With formats 1 and 2 ("inline footnotes") text of a footnote is provided
170 > in the place where the corresponding numeric mark will be rendered.
171 > With formats 3, 4, and 5 ("reference footnotes") text of a footnote
172 > is supplied elsewhere in the document, as shown below.
173 > Formats 2, 4 and 5 ("span-specific footnotes") mark a specific fragment
174 > that is being commented in the footnote.
175 > Format 5 reuses a fragment of text as a label.
176 > Labels are case-insensitive.
177
178 >
179 ```
180 [^label]: Footnote definition must start on the first column.
181 The second line (if any) must be indented by two or more spaces.
182 Definition continues until indentation drops below that of the 2nd line.
183 ```
184 > Charachter **^** is not part of a label, it is part of the syntax.
185 > Both a footnote's text and a fragment to which a footnote applies
186 > are subject to further interpretation as Markdown sources.
187
188 ## Miscellaneous ##
189
190 > * In-line images are made using **\!\[alt-text\]\(image-URL\)**.
191 > * Use HTML for advanced formatting such as forms.
192 > * **\<!--** HTML-style comments **-->** are supported.
193
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -325,23 +325,32 @@
325325
BLOB_APPEND_BLOB(ob, cells);
326326
BLOB_APPEND_LITERAL(ob, " </tr>\n");
327327
}
328328
329329
static int html_footnote_ref(
330
- struct Blob *ob, int index, int locus, void *opaque
330
+ struct Blob *ob, const struct Blob *span, int index, int locus, void *opaque
331331
){
332332
const struct MarkdownToHtml *ctx = (struct MarkdownToHtml*)opaque;
333333
const bitfield64_t l = to_base26(locus-1,0);
334334
char pos[32];
335335
336336
/* expect BUGs if the following yields compiler warnings */
337337
memset(pos,0,32);
338338
sprintf(pos, "%s%i-%s", ctx->unique.c, index, l.c);
339
-
340
- BLOB_APPEND_LITERAL(ob,"<a class='noteref' href='#footnote-");
341
- blob_appendf(ob,"%s' id='noteref-%s'><sup>%i</sup></a>",
342
- pos, pos, index);
339
+ if(span && blob_size(span)) {
340
+ BLOB_APPEND_LITERAL(ob,"<span class='notescope' id='noteref-");
341
+ blob_appendf(ob,"%s'>",pos);
342
+ BLOB_APPEND_BLOB(ob, span);
343
+ blob_trim(ob);
344
+ BLOB_APPEND_LITERAL(ob,"<a class='noteref' href='#footnote-");
345
+ blob_appendf(ob,"%s'><sup>%i</sup></a></span>", pos, index);
346
+ }else{
347
+ blob_trim(ob);
348
+ BLOB_APPEND_LITERAL(ob,"<a class='noteref' href='#footnote-");
349
+ blob_appendf(ob,"%s' id='noteref-%s'><sup>%i</sup></a>",
350
+ pos, pos, index);
351
+ }
343352
return 1;
344353
}
345354
346355
/* Render a single item of the footnotes list.
347356
* Each backref gets a unique id to enable dynamic styling. */
348357
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -325,23 +325,32 @@
325 BLOB_APPEND_BLOB(ob, cells);
326 BLOB_APPEND_LITERAL(ob, " </tr>\n");
327 }
328
329 static int html_footnote_ref(
330 struct Blob *ob, int index, int locus, void *opaque
331 ){
332 const struct MarkdownToHtml *ctx = (struct MarkdownToHtml*)opaque;
333 const bitfield64_t l = to_base26(locus-1,0);
334 char pos[32];
335
336 /* expect BUGs if the following yields compiler warnings */
337 memset(pos,0,32);
338 sprintf(pos, "%s%i-%s", ctx->unique.c, index, l.c);
339
340 BLOB_APPEND_LITERAL(ob,"<a class='noteref' href='#footnote-");
341 blob_appendf(ob,"%s' id='noteref-%s'><sup>%i</sup></a>",
342 pos, pos, index);
 
 
 
 
 
 
 
 
 
343 return 1;
344 }
345
346 /* Render a single item of the footnotes list.
347 * Each backref gets a unique id to enable dynamic styling. */
348
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -325,23 +325,32 @@
325 BLOB_APPEND_BLOB(ob, cells);
326 BLOB_APPEND_LITERAL(ob, " </tr>\n");
327 }
328
329 static int html_footnote_ref(
330 struct Blob *ob, const struct Blob *span, int index, int locus, void *opaque
331 ){
332 const struct MarkdownToHtml *ctx = (struct MarkdownToHtml*)opaque;
333 const bitfield64_t l = to_base26(locus-1,0);
334 char pos[32];
335
336 /* expect BUGs if the following yields compiler warnings */
337 memset(pos,0,32);
338 sprintf(pos, "%s%i-%s", ctx->unique.c, index, l.c);
339 if(span && blob_size(span)) {
340 BLOB_APPEND_LITERAL(ob,"<span class='notescope' id='noteref-");
341 blob_appendf(ob,"%s'>",pos);
342 BLOB_APPEND_BLOB(ob, span);
343 blob_trim(ob);
344 BLOB_APPEND_LITERAL(ob,"<a class='noteref' href='#footnote-");
345 blob_appendf(ob,"%s'><sup>%i</sup></a></span>", pos, index);
346 }else{
347 blob_trim(ob);
348 BLOB_APPEND_LITERAL(ob,"<a class='noteref' href='#footnote-");
349 blob_appendf(ob,"%s' id='noteref-%s'><sup>%i</sup></a>",
350 pos, pos, index);
351 }
352 return 1;
353 }
354
355 /* Render a single item of the footnotes list.
356 * Each backref gets a unique id to enable dynamic styling. */
357

Keyboard Shortcuts

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