diff --git a/lib/jsdoc/util/markdown.js b/lib/jsdoc/util/markdown.js index 766bffdd..3f1389b3 100644 --- a/lib/jsdoc/util/markdown.js +++ b/lib/jsdoc/util/markdown.js @@ -9,6 +9,7 @@ var logger = require('jsdoc/util/logger'); var MarkdownIt = require('markdown-it'); var marked = require('marked'); var mdnh = require('markdown-it-named-headers'); +var path = require('jsdoc/path'); 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 * 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. */ function getParseFunction(parserName, conf) { + var highlighter; var parserFunction; var renderer; conf = conf || {}; + highlighter = getHighlighter(conf); switch (parserName) { case parserNames.marked: @@ -168,7 +216,7 @@ function getParseFunction(parserName, conf) { }; } - renderer.code = highlight; + renderer.code = highlighter; parserFunction = function(source) { var result; @@ -192,7 +240,7 @@ function getParseFunction(parserName, conf) { case parserNames.markdownit: renderer = new MarkdownIt({ breaks: Boolean(conf.hardwrap), - highlight: highlight, + highlight: highlighter, html: true }); diff --git a/test/fixtures/markdown/badhighlighter.js b/test/fixtures/markdown/badhighlighter.js new file mode 100644 index 00000000..d734b9b2 --- /dev/null +++ b/test/fixtures/markdown/badhighlighter.js @@ -0,0 +1,3 @@ +'use strict'; + +exports.highlight = 'Not a real highlighter'; diff --git a/test/fixtures/markdown/highlighter.js b/test/fixtures/markdown/highlighter.js new file mode 100644 index 00000000..637361c5 --- /dev/null +++ b/test/fixtures/markdown/highlighter.js @@ -0,0 +1,5 @@ +'use strict'; + +exports.highlight = function(code, language) { + return '
' + code + ' in this language: ' + language + '
'; +}; diff --git a/test/specs/jsdoc/util/markdown.js b/test/specs/jsdoc/util/markdown.js index 9ee116de..a3739dc7 100644 --- a/test/specs/jsdoc/util/markdown.js +++ b/test/specs/jsdoc/util/markdown.js @@ -2,6 +2,7 @@ describe('jsdoc/util/markdown', function() { var env = require('jsdoc/env'); + var logger = require('jsdoc/util/logger'); var markdown = require('jsdoc/util/markdown'); 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() { - var logger = require('jsdoc/util/logger'); - setMarkdownConf({parser: 'not-a-real-markdown-parser'}); spyOn(logger, 'error'); markdown.getParser(); + expect(logger.error).toHaveBeenCalled(); }); @@ -131,5 +131,53 @@ describe('jsdoc/util/markdown', function() { 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 '
' + code + ' highlighted as ' + language +
+                            '
'; + } + }); + parser = markdown.getParser(); + + expect(parser('```js\nhello\n```')).toBe( + '
hello\n highlighted as js
' + ); + }); + + 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( + '
hello\n in this language: js
' + ); + }); + + 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(); + }); + }); }); });