259 lines
4.8 KiB
JavaScript
259 lines
4.8 KiB
JavaScript
|
/**
|
||
|
* @param {string} value
|
||
|
* @returns {RegExp}
|
||
|
* */
|
||
|
|
||
|
/**
|
||
|
* @param {RegExp | string } re
|
||
|
* @returns {string}
|
||
|
*/
|
||
|
function source(re) {
|
||
|
if (!re) return null;
|
||
|
if (typeof re === "string") return re;
|
||
|
|
||
|
return re.source;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param {...(RegExp | string) } args
|
||
|
* @returns {string}
|
||
|
*/
|
||
|
function concat(...args) {
|
||
|
const joined = args.map((x) => source(x)).join("");
|
||
|
return joined;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Language: Markdown
|
||
|
Requires: xml.js
|
||
|
Author: John Crepezzi <john.crepezzi@gmail.com>
|
||
|
Website: https://daringfireball.net/projects/markdown/
|
||
|
Category: common, markup
|
||
|
*/
|
||
|
|
||
|
function markdown(hljs) {
|
||
|
const INLINE_HTML = {
|
||
|
begin: /<\/?[A-Za-z_]/,
|
||
|
end: '>',
|
||
|
subLanguage: 'xml',
|
||
|
relevance: 0
|
||
|
};
|
||
|
const HORIZONTAL_RULE = {
|
||
|
begin: '^[-\\*]{3,}',
|
||
|
end: '$'
|
||
|
};
|
||
|
const CODE = {
|
||
|
className: 'code',
|
||
|
variants: [
|
||
|
// TODO: fix to allow these to work with sublanguage also
|
||
|
{
|
||
|
begin: '(`{3,})[^`](.|\\n)*?\\1`*[ ]*'
|
||
|
},
|
||
|
{
|
||
|
begin: '(~{3,})[^~](.|\\n)*?\\1~*[ ]*'
|
||
|
},
|
||
|
// needed to allow markdown as a sublanguage to work
|
||
|
{
|
||
|
begin: '```',
|
||
|
end: '```+[ ]*$'
|
||
|
},
|
||
|
{
|
||
|
begin: '~~~',
|
||
|
end: '~~~+[ ]*$'
|
||
|
},
|
||
|
{
|
||
|
begin: '`.+?`'
|
||
|
},
|
||
|
{
|
||
|
begin: '(?=^( {4}|\\t))',
|
||
|
// use contains to gobble up multiple lines to allow the block to be whatever size
|
||
|
// but only have a single open/close tag vs one per line
|
||
|
contains: [
|
||
|
{
|
||
|
begin: '^( {4}|\\t)',
|
||
|
end: '(\\n)$'
|
||
|
}
|
||
|
],
|
||
|
relevance: 0
|
||
|
}
|
||
|
]
|
||
|
};
|
||
|
const LIST = {
|
||
|
className: 'bullet',
|
||
|
begin: '^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)',
|
||
|
end: '\\s+',
|
||
|
excludeEnd: true
|
||
|
};
|
||
|
const LINK_REFERENCE = {
|
||
|
begin: /^\[[^\n]+\]:/,
|
||
|
returnBegin: true,
|
||
|
contains: [
|
||
|
{
|
||
|
className: 'symbol',
|
||
|
begin: /\[/,
|
||
|
end: /\]/,
|
||
|
excludeBegin: true,
|
||
|
excludeEnd: true
|
||
|
},
|
||
|
{
|
||
|
className: 'link',
|
||
|
begin: /:\s*/,
|
||
|
end: /$/,
|
||
|
excludeBegin: true
|
||
|
}
|
||
|
]
|
||
|
};
|
||
|
const URL_SCHEME = /[A-Za-z][A-Za-z0-9+.-]*/;
|
||
|
const LINK = {
|
||
|
variants: [
|
||
|
// too much like nested array access in so many languages
|
||
|
// to have any real relevance
|
||
|
{
|
||
|
begin: /\[.+?\]\[.*?\]/,
|
||
|
relevance: 0
|
||
|
},
|
||
|
// popular internet URLs
|
||
|
{
|
||
|
begin: /\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/,
|
||
|
relevance: 2
|
||
|
},
|
||
|
{
|
||
|
begin: concat(/\[.+?\]\(/, URL_SCHEME, /:\/\/.*?\)/),
|
||
|
relevance: 2
|
||
|
},
|
||
|
// relative urls
|
||
|
{
|
||
|
begin: /\[.+?\]\([./?&#].*?\)/,
|
||
|
relevance: 1
|
||
|
},
|
||
|
// whatever else, lower relevance (might not be a link at all)
|
||
|
{
|
||
|
begin: /\[.+?\]\(.*?\)/,
|
||
|
relevance: 0
|
||
|
}
|
||
|
],
|
||
|
returnBegin: true,
|
||
|
contains: [
|
||
|
{
|
||
|
className: 'string',
|
||
|
relevance: 0,
|
||
|
begin: '\\[',
|
||
|
end: '\\]',
|
||
|
excludeBegin: true,
|
||
|
returnEnd: true
|
||
|
},
|
||
|
{
|
||
|
className: 'link',
|
||
|
relevance: 0,
|
||
|
begin: '\\]\\(',
|
||
|
end: '\\)',
|
||
|
excludeBegin: true,
|
||
|
excludeEnd: true
|
||
|
},
|
||
|
{
|
||
|
className: 'symbol',
|
||
|
relevance: 0,
|
||
|
begin: '\\]\\[',
|
||
|
end: '\\]',
|
||
|
excludeBegin: true,
|
||
|
excludeEnd: true
|
||
|
}
|
||
|
]
|
||
|
};
|
||
|
const BOLD = {
|
||
|
className: 'strong',
|
||
|
contains: [], // defined later
|
||
|
variants: [
|
||
|
{
|
||
|
begin: /_{2}/,
|
||
|
end: /_{2}/
|
||
|
},
|
||
|
{
|
||
|
begin: /\*{2}/,
|
||
|
end: /\*{2}/
|
||
|
}
|
||
|
]
|
||
|
};
|
||
|
const ITALIC = {
|
||
|
className: 'emphasis',
|
||
|
contains: [], // defined later
|
||
|
variants: [
|
||
|
{
|
||
|
begin: /\*(?!\*)/,
|
||
|
end: /\*/
|
||
|
},
|
||
|
{
|
||
|
begin: /_(?!_)/,
|
||
|
end: /_/,
|
||
|
relevance: 0
|
||
|
}
|
||
|
]
|
||
|
};
|
||
|
BOLD.contains.push(ITALIC);
|
||
|
ITALIC.contains.push(BOLD);
|
||
|
|
||
|
let CONTAINABLE = [
|
||
|
INLINE_HTML,
|
||
|
LINK
|
||
|
];
|
||
|
|
||
|
BOLD.contains = BOLD.contains.concat(CONTAINABLE);
|
||
|
ITALIC.contains = ITALIC.contains.concat(CONTAINABLE);
|
||
|
|
||
|
CONTAINABLE = CONTAINABLE.concat(BOLD, ITALIC);
|
||
|
|
||
|
const HEADER = {
|
||
|
className: 'section',
|
||
|
variants: [
|
||
|
{
|
||
|
begin: '^#{1,6}',
|
||
|
end: '$',
|
||
|
contains: CONTAINABLE
|
||
|
},
|
||
|
{
|
||
|
begin: '(?=^.+?\\n[=-]{2,}$)',
|
||
|
contains: [
|
||
|
{
|
||
|
begin: '^[=-]*$'
|
||
|
},
|
||
|
{
|
||
|
begin: '^',
|
||
|
end: "\\n",
|
||
|
contains: CONTAINABLE
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
]
|
||
|
};
|
||
|
|
||
|
const BLOCKQUOTE = {
|
||
|
className: 'quote',
|
||
|
begin: '^>\\s+',
|
||
|
contains: CONTAINABLE,
|
||
|
end: '$'
|
||
|
};
|
||
|
|
||
|
return {
|
||
|
name: 'Markdown',
|
||
|
aliases: [
|
||
|
'md',
|
||
|
'mkdown',
|
||
|
'mkd'
|
||
|
],
|
||
|
contains: [
|
||
|
HEADER,
|
||
|
INLINE_HTML,
|
||
|
LIST,
|
||
|
BOLD,
|
||
|
ITALIC,
|
||
|
BLOCKQUOTE,
|
||
|
CODE,
|
||
|
HORIZONTAL_RULE,
|
||
|
LINK,
|
||
|
LINK_REFERENCE
|
||
|
]
|
||
|
};
|
||
|
}
|
||
|
|
||
|
module.exports = markdown;
|