allow users to specify a highlighter for Markdown code blocks (#1412)

This commit is contained in:
Jeff Williams 2017-07-23 15:01:27 -07:00
parent f3a31e9a4c
commit 2c47d4b306
4 changed files with 108 additions and 4 deletions

View File

@ -9,6 +9,7 @@ var logger = require('jsdoc/util/logger');
var MarkdownIt = require('markdown-it'); var MarkdownIt = require('markdown-it');
var marked = require('marked'); var marked = require('marked');
var mdnh = require('markdown-it-named-headers'); var mdnh = require('markdown-it-named-headers');
var path = require('jsdoc/path');
var util = require('util'); var util = require('util');
/** /**
@ -137,6 +138,51 @@ function unencodeQuotes(source) {
}); });
} }
/**
* Get the appropriate function for applying syntax highlighting to text, based on the user's
* Markdown configuration settings.
*
* @param {Object} conf - The user's Markdown configuration settings.
* @return {function} The highlighter function.
*/
function getHighlighter(conf) {
var highlighter;
var highlighterPath;
switch (typeof conf.highlight) {
case 'string':
highlighterPath = path.getResourcePath(conf.highlight);
if (highlighterPath) {
highlighter = require(highlighterPath).highlight;
if (typeof highlighter !== 'function') {
logger.error('The syntax highlighting module "%s" does not assign a method ' +
'to exports.highlight. Using the default syntax highlighter.',
conf.highlight);
highlighter = highlight;
}
}
else {
logger.error('Unable to find the syntax highlighting module "%s". Using the ' +
'default syntax highlighter.', conf.highlight);
highlighter = highlight;
}
break;
case 'function':
highlighter = conf.highlight;
break;
default:
highlighter = highlight;
}
return highlighter;
}
/** /**
* Retrieve a function that accepts a single parameter containing Markdown source. The function uses * Retrieve a function that accepts a single parameter containing Markdown source. The function uses
* the specified parser to transform the Markdown source to HTML, then returns the HTML as a string. * the specified parser to transform the Markdown source to HTML, then returns the HTML as a string.
@ -148,10 +194,12 @@ function unencodeQuotes(source) {
* returns the resulting HTML. * returns the resulting HTML.
*/ */
function getParseFunction(parserName, conf) { function getParseFunction(parserName, conf) {
var highlighter;
var parserFunction; var parserFunction;
var renderer; var renderer;
conf = conf || {}; conf = conf || {};
highlighter = getHighlighter(conf);
switch (parserName) { switch (parserName) {
case parserNames.marked: case parserNames.marked:
@ -168,7 +216,7 @@ function getParseFunction(parserName, conf) {
}; };
} }
renderer.code = highlight; renderer.code = highlighter;
parserFunction = function(source) { parserFunction = function(source) {
var result; var result;
@ -192,7 +240,7 @@ function getParseFunction(parserName, conf) {
case parserNames.markdownit: case parserNames.markdownit:
renderer = new MarkdownIt({ renderer = new MarkdownIt({
breaks: Boolean(conf.hardwrap), breaks: Boolean(conf.hardwrap),
highlight: highlight, highlight: highlighter,
html: true html: true
}); });

View File

@ -0,0 +1,3 @@
'use strict';
exports.highlight = 'Not a real highlighter';

5
test/fixtures/markdown/highlighter.js vendored Normal file
View File

@ -0,0 +1,5 @@
'use strict';
exports.highlight = function(code, language) {
return '<pre><code>' + code + ' in this language: ' + language + '</code></pre>';
};

View File

@ -2,6 +2,7 @@
describe('jsdoc/util/markdown', function() { describe('jsdoc/util/markdown', function() {
var env = require('jsdoc/env'); var env = require('jsdoc/env');
var logger = require('jsdoc/util/logger');
var markdown = require('jsdoc/util/markdown'); var markdown = require('jsdoc/util/markdown');
it('should exist', function() { it('should exist', function() {
@ -66,11 +67,10 @@ describe('jsdoc/util/markdown', function() {
}); });
it('should log an error if an unrecognized Markdown parser is requested', function() { it('should log an error if an unrecognized Markdown parser is requested', function() {
var logger = require('jsdoc/util/logger');
setMarkdownConf({parser: 'not-a-real-markdown-parser'}); setMarkdownConf({parser: 'not-a-real-markdown-parser'});
spyOn(logger, 'error'); spyOn(logger, 'error');
markdown.getParser(); markdown.getParser();
expect(logger.error).toHaveBeenCalled(); expect(logger.error).toHaveBeenCalled();
}); });
@ -131,5 +131,53 @@ describe('jsdoc/util/markdown', function() {
expect(parser(markdownText)).toBe(convertedText); expect(parser(markdownText)).toBe(convertedText);
}); });
describe('syntax highlighter', function() {
it('should support a `highlight` function defined in the config file', function() {
var parser;
setMarkdownConf({
highlight: function(code, language) {
return '<pre><code>' + code + ' highlighted as ' + language +
'</code></pre>';
}
});
parser = markdown.getParser();
expect(parser('```js\nhello\n```')).toBe(
'<pre><code>hello\n highlighted as js</code></pre>'
);
});
it('should support `highlight` as the path to a highlighter module', function() {
var parser;
setMarkdownConf({ highlight: 'test/fixtures/markdown/highlighter' });
parser = markdown.getParser();
expect(parser('```js\nhello\n```')).toBe(
'<pre><code>hello\n in this language: js</code></pre>'
);
});
it('should log an error if the `highlight` module cannot be found', function() {
spyOn(logger, 'error');
setMarkdownConf({ highlight: 'foo/bar/baz' });
markdown.getParser();
expect(logger.error).toHaveBeenCalled();
});
it('should log an error if the `highlight` module does not assign a method to ' +
'`exports.highlight`', function() {
spyOn(logger, 'error');
setMarkdownConf({ highlight: 'test/fixtures/markdown/badhighlighter' });
markdown.getParser();
expect(logger.error).toHaveBeenCalled();
});
});
}); });
}); });