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;
 |