Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Enhance markdown to use the same hyperlink target resolving logic as Fossil wiki. That means that wiki page names and check-in and ticket hashes can now be used as markdown hyperlink URLs. Also enhance markdown hyperlinks so that if the display text is an empty string, the URL is used as the display text. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
774fb7712fb43fe3a59b168454f18162 |
| User & Date: | drh 2019-08-09 18:14:35.615 |
Context
|
2019-08-09
| ||
| 20:56 | Fixes to the new markdown hyperlink logic to handle the case there the target URL is not defined. ... (check-in: 44545eef88 user: drh tags: trunk) | |
| 20:15 | Contributing PowerShell script Set-ChildProject.ps1. This script wraps the code for converting a project into a child project into an easy to use PowerShell script. The script should be cross platform, but was only tested on Windows 10. ... (Closed-Leaf check-in: 0b70cf6d57 user: ckennedy tags: windows-tools) | |
| 18:14 | Enhance markdown to use the same hyperlink target resolving logic as Fossil wiki. That means that wiki page names and check-in and ticket hashes can now be used as markdown hyperlink URLs. Also enhance markdown hyperlinks so that if the display text is an empty string, the URL is used as the display text. ... (check-in: 774fb7712f user: drh tags: trunk) | |
| 16:40 | Two additional places where links to /markup_help are useful. ... (check-in: 5f328d9f90 user: drh tags: trunk) | |
Changes
Changes to src/markdown.md.
| ︙ | ︙ | |||
24 25 26 27 28 29 30 31 | > 1. **\[display text\]\(URL\)** > 2. **\[display text\]\(URL "Title"\)** > 3. **\[display text\]\(URL 'Title'\)** > 4. **\<URL\>** > 5. **\[display text\]\[label\]** > 6. **\[display text\]\[\]** > 7. **\[display text\]** | > | | | | < > > > > > > > > > > > > > | 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 50 51 52 53 54 55 56 57 58 59 60 61 62 |
> 1. **\[display text\]\(URL\)**
> 2. **\[display text\]\(URL "Title"\)**
> 3. **\[display text\]\(URL 'Title'\)**
> 4. **\<URL\>**
> 5. **\[display text\]\[label\]**
> 6. **\[display text\]\[\]**
> 7. **\[display text\]**
> 8. **\[\]\(URL\)**
> With link formats 5, 6, and 7 ("reference links"), the URL is supplied
> elsewhere in the document, as shown below. Link formats 6 and 7 reuse
> the display text as the label. Labels are case-insensitive. The title
> may be split onto the next line with optional indenting.
> * **\[label\]: URL**
> * **\[label\]: URL "Title"**
> * **\[label\]: URL 'Title'**
> * **\[label\]: URL (Title)**
> **URL** may optionally be written **\<URL\>**.
> In addition to ordinary URLs, the **URL** may be:
> <ul>
> <li> A pathname starting with "/" in which case the Fossil server
> URL prefix is prepended
> <li> A wiki page name, or a wiki page name preceded by "wiki:"
> <li> An artifact or ticket hash or hash prefix
> <li> A date and time stamp: "YYYY-MM-DD HH:MM:SS" or a subset that
> includes at least the day of the month.</ul>
> In form 8, then the URL becomes the display text. This is useful for
> hyperlinks that refer to wiki pages and check-in and ticket hashes.
## Fonts ##
> * _\*italic\*_
> * *\_italic\_*
> * __\*\*bold\*\*__
> * **\_\_bold\_\_**
> * ___\*\*\*italic+bold\*\*\*___
|
| ︙ | ︙ |
Changes to src/markdown_html.c.
| ︙ | ︙ | |||
425 426 427 428 429 430 431 |
struct Blob *ob,
struct Blob *link,
struct Blob *title,
struct Blob *content,
void *opaque
){
char *zLink = blob_buffer(link);
| | | < < < | < > | | < | < | > | | 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
struct Blob *ob,
struct Blob *link,
struct Blob *title,
struct Blob *content,
void *opaque
){
char *zLink = blob_buffer(link);
char *zTitle = title!=0 && blob_size(title)>0 ? blob_str(title) : 0;
char zClose[20];
wiki_resolve_hyperlink(ob, 0, zLink, zClose, sizeof(zClose), 0, zTitle);
if( blob_size(content)==0 ){
BLOB_APPEND_BLOB(ob, link);
}else{
BLOB_APPEND_BLOB(ob, content);
}
blob_append(ob, zClose, -1);
return 1;
}
static int html_triple_emphasis(
struct Blob *ob,
struct Blob *text,
char c,
|
| ︙ | ︙ |
Changes to src/style.c.
| ︙ | ︙ | |||
140 141 142 143 144 145 146 |
char *xhref(const char *zExtra, const char *zFormat, ...){
char *zUrl;
va_list ap;
va_start(ap, zFormat);
zUrl = vmprintf(zFormat, ap);
va_end(ap);
if( g.perm.Hyperlink && !g.javascriptHyperlink ){
| > > | > > > > > > | | > | 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
char *xhref(const char *zExtra, const char *zFormat, ...){
char *zUrl;
va_list ap;
va_start(ap, zFormat);
zUrl = vmprintf(zFormat, ap);
va_end(ap);
if( g.perm.Hyperlink && !g.javascriptHyperlink ){
char *zHUrl;
if( zExtra ){
zHUrl = mprintf("<a %s href=\"%h\">", zExtra, zUrl);
}else{
zHUrl = mprintf("<a href=\"%h\">", zUrl);
}
fossil_free(zUrl);
return zHUrl;
}
needHrefJs = 1;
if( zExtra==0 ){
return mprintf("<a data-href='%z' href='%R/honeypot'>", zUrl);
}else{
return mprintf("<a %s data-href='%z' href='%R/honeypot'>",
zExtra, zUrl);
}
}
char *chref(const char *zExtra, const char *zFormat, ...){
char *zUrl;
va_list ap;
va_start(ap, zFormat);
zUrl = vmprintf(zFormat, ap);
va_end(ap);
|
| ︙ | ︙ |
Changes to src/wikiformat.c.
| ︙ | ︙ | |||
1140 1141 1142 1143 1144 1145 1146 | } /* ** Return a pointer to the name part of zTarget (skipping the "wiki:" prefix ** if there is one) if zTarget is a valid wiki page name. Return NULL if ** zTarget names a page that does not exist. */ | | | | 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 |
}
/*
** Return a pointer to the name part of zTarget (skipping the "wiki:" prefix
** if there is one) if zTarget is a valid wiki page name. Return NULL if
** zTarget names a page that does not exist.
*/
static const char *validWikiPageName(int mFlags, const char *zTarget){
if( strncmp(zTarget, "wiki:", 5)==0
&& wiki_name_is_wellformed((const unsigned char*)zTarget) ){
return zTarget+5;
}
if( strcmp(zTarget, "Sandbox")==0 ) return zTarget;
if( wiki_name_is_wellformed((const unsigned char *)zTarget)
&& ((mFlags & WIKI_NOBADLINKS)==0 ||
db_exists("SELECT 1 FROM tag WHERE tagname GLOB 'wiki-%q'"
" AND (SELECT value FROM tagxref WHERE tagid=tag.tagid"
" ORDER BY mtime DESC LIMIT 1) > 0", zTarget))
){
return zTarget;
}
return 0;
|
| ︙ | ︙ | |||
1222 1223 1224 1225 1226 1227 1228 | ** ** [0123456789abcdef] ** ** [#fragment] ** ** [2010-02-27 07:13] */ | | > | | > > > > > > > | | | | | | | | | | | | | | | | | | | | > | 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 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 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 |
**
** [0123456789abcdef]
**
** [#fragment]
**
** [2010-02-27 07:13]
*/
void wiki_resolve_hyperlink(
Blob *pOut, /* Write the HTML output here */
int mFlags, /* Rendering option flags */
const char *zTarget, /* Hyperlink target; text within [...] */
char *zClose, /* Write hyperlink closing text here */
int nClose, /* Bytes available in zClose[] */
const char *zOrig, /* Complete document text */
const char *zTitle /* Title of the link */
){
const char *zTerm = "</a>";
const char *z;
char *zExtra = 0;
const char *zExtraNS = 0;
if( zTitle ){
zExtra = mprintf(" title='%h'", zTitle);
zExtraNS = zExtra+1;
}
assert( nClose>=20 );
if( strncmp(zTarget, "http:", 5)==0
|| strncmp(zTarget, "https:", 6)==0
|| strncmp(zTarget, "ftp:", 4)==0
|| strncmp(zTarget, "mailto:", 7)==0
){
blob_appendf(pOut, "<a href=\"%s\"%s>", zTarget, zExtra);
}else if( zTarget[0]=='/' ){
blob_appendf(pOut, "<a href=\"%R%h\"%s>", zTarget, zExtra);
}else if( zTarget[0]=='.'
&& (zTarget[1]=='/' || (zTarget[1]=='.' && zTarget[2]=='/'))
&& (mFlags & WIKI_LINKSONLY)==0 ){
blob_appendf(pOut, "<a href=\"%h\"%s>", zTarget, zExtra);
}else if( zTarget[0]=='#' ){
blob_appendf(pOut, "<a href=\"%h\"%s>", zTarget, zExtra);
}else if( is_valid_hname(zTarget) ){
int isClosed = 0;
if( strlen(zTarget)<=HNAME_MAX && is_ticket(zTarget, &isClosed) ){
/* Special display processing for tickets. Display the hyperlink
** as crossed out if the ticket is closed.
*/
if( isClosed ){
if( g.perm.Hyperlink ){
blob_appendf(pOut,
"%z<span class=\"wikiTagCancelled\">[",
xhref(zExtraNS,"%R/info/%s",zTarget)
);
zTerm = "]</span></a>";
}else{
blob_appendf(pOut,"<span class=\"wikiTagCancelled\">[");
zTerm = "]</span>";
}
}else{
if( g.perm.Hyperlink ){
blob_appendf(pOut,"%z[", xhref(zExtraNS,"%R/info/%s", zTarget));
zTerm = "]</a>";
}else{
blob_appendf(pOut, "[");
zTerm = "]";
}
}
}else if( !in_this_repo(zTarget) ){
if( (mFlags & (WIKI_LINKSONLY|WIKI_NOBADLINKS))!=0 ){
zTerm = "";
}else{
blob_appendf(pOut, "<span class=\"brokenlink\">[");
zTerm = "]</span>";
}
}else if( g.perm.Hyperlink ){
blob_appendf(pOut, "%z[",xhref(zExtraNS, "%R/info/%s", zTarget));
zTerm = "]</a>";
}else{
zTerm = "";
}
}else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
&& db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
blob_appendf(pOut, "<a href=\"%R/timeline?c=%T\"%s>", zTarget, zExtra);
}else if( (z = validWikiPageName(mFlags, zTarget))!=0 ){
const char *zOverride = wiki_is_overridden(zTarget);
if( zOverride ){
blob_appendf(pOut, "<a href=\"%R/info/%S\"%s>", zOverride, zExtra);
}else{
blob_appendf(pOut, "<a href=\"%R/wiki?name=%T\"%s>", z, zExtra);
}
}else if( zOrig && zTarget>=&zOrig[2] && !fossil_isspace(zTarget[-2]) ){
/* Probably an array subscript in code */
zTerm = "";
}else if( (mFlags & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){
zTerm = "";
}else{
blob_appendf(pOut, "<span class=\"brokenlink\">[%h]", zTarget);
zTerm = "</span>";
}
if( zExtra ) fossil_free(zExtra);
assert( strlen(zTerm)<nClose );
sqlite3_snprintf(nClose, zClose, "%s", zTerm);
}
/*
** Check to see if the given parsed markup is the correct
** </verbatim> tag.
|
| ︙ | ︙ | |||
1489 1490 1491 1492 1493 1494 1495 |
}
z[i] = 0;
if( zDisplay==0 ){
zDisplay = zTarget;
}else{
while( fossil_isspace(*zDisplay) ) zDisplay++;
}
| > | | 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 |
}
z[i] = 0;
if( zDisplay==0 ){
zDisplay = zTarget;
}else{
while( fossil_isspace(*zDisplay) ) zDisplay++;
}
wiki_resolve_hyperlink(p->pOut, p->state,
zTarget, zClose, sizeof(zClose), zOrig, 0);
if( linksOnly || zClose[0]==0 || p->inVerbatim ){
if( cS1 ) z[iS1] = cS1;
if( zClose[0]!=']' ){
blob_appendf(p->pOut, "[%h]%s", zTarget, zClose);
}else{
blob_appendf(p->pOut, "%h%s", zTarget, zClose);
}
|
| ︙ | ︙ |