diff --git a/README.md b/README.md index fcf41ff4..177ed624 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # markdown-it-prism [![Build Status](https://travis-ci.org/jGleitz/markdown-it-prism.svg?branch=master)](https://travis-ci.org/jGleitz/markdown-it-prism) [![npm version](https://badge.fury.io/js/markdown-it-prism.svg)](https://badge.fury.io/js/markdown-it-prism) [![Bower version](https://badge.fury.io/bo/markdown-it-prism.svg)](https://badge.fury.io/bo/markdown-it-prism) + > [markdown-it](https://github.com/markdown-it/markdown-it) plugin to highlight code blocks using [Prism](http://prismjs.com/) ## Usage + ```js const md = require('markdown-it')(); const prism = require('markdown-it-prism'); @@ -9,20 +11,37 @@ const prism = require('markdown-it-prism'); md.use(prism, options); ``` -The plugin will insert the necessary markup into all code blocks. [Include one of Prism’s stylesheets](http://prismjs.com/#basic-usage) in your HTML to get highlighted code. +The plugin will insert the necessary markup into all code blocks. [Include one of Prism’s stylesheets](http://prismjs.com/#basic-usage) in +your HTML to get highlighted code. ### Options + The `options` object may contain: -Name | Description | Default --------|-------------|-------- -`plugins` | Array of [Prism Plugins](http://prismjs.com/#plugins) to load. The names to use can be found [here](https://github.com/PrismJS/prism/tree/master/plugins). Please note that some prism plugins (notably line-numbers) rely on the DOM being present and can thus not be used with this package (see [#1](https://github.com/jGleitz/markdown-it-prism/issues/1)). | `[]` -`init` | A function called after setting up prism. Will receive the prism instance as only argument. Useful for plugins needing further intialisation. | `() => {}` -`defaultLanguageForUnknown` | The language to use for code blocks that specify a language that Prism does not know. No default will be used if this option is `undefined`. | `undefined` -`defaultLanguageForUnspecified` | The language to use for code block that do not specify a language. No default will be used if this option is `undefined`. | `undefined` -`defaultLanguage` | Shorthand to set both `defaultLanguageForUnknown` and `defaultLanguageForUnspecified` to the same value. | `undefined` +| Name | Description | Default | +|---------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------| +| `highlightInlineCode` | Whether to highlight inline code. | `false` | +| `plugins` | Array of [Prism Plugins](http://prismjs.com/#plugins) to load. The names to use can be found [here](https://github.com/PrismJS/prism/tree/master/plugins). Please note that some prism plugins (notably line-numbers) rely on the DOM being present and can thus not be used with this package (see [#1](https://github.com/jGleitz/markdown-it-prism/issues/1)). | `[]` | +| `init` | A function called after setting up prism. Will receive the prism instance as only argument. Useful for plugins needing further intialisation. | `() => {}` | +| `defaultLanguageForUnknown` | The language to use for code blocks that specify a language that Prism does not know. No default will be used if this option is `undefined`. | `undefined` | +| `defaultLanguageForUnspecified` | The language to use for code block that do not specify a language. No default will be used if this option is `undefined`. | `undefined` | +| `defaultLanguage` | Shorthand to set both `defaultLanguageForUnknown` and `defaultLanguageForUnspecified` to the same value. | `undefined` | + +### Inline Code + +When `highlightInlineCode` is set, inline code will be highlighted just like fenced code blocks are. +To specifiy the language of inline code, add `{language=}` after the code segment: + +```markdown +`class Demo { };`{language=cpp} +``` + +This syntax is compatible with [markdown-it-attrs](https://github.com/arve0/markdown-it-attrs): +The `language=` part will be stripped, but everything else between `{` and `}` will work +with [markdown-it-attrs](https://github.com/arve0/markdown-it-attrs) as usual. ## Usage with Webpack + If you want to use this plugin together with [Webpack](https://webpack.js.org/), you need to import all languages you intend to use: ```javascript @@ -33,10 +52,10 @@ import "prismjs/components/prism-clike" import "prismjs/components/prism-java" function component() { - const md = new MarkdownIt(); - md.use(prism); - const element = document.createElement('div'); - element.innerHTML = md.render(` + const md = new MarkdownIt(); + md.use(prism); + const element = document.createElement('div'); + element.innerHTML = md.render(` Here is some *code*: \`\`\`java public class Test { @@ -45,10 +64,11 @@ public class Test { \`\`\` `); - return element; + return element; } document.body.appendChild(component()); ``` -*Beware*: Prisms languages have dependencies onto each other. You need to import the languages together with their dependencies in the correct order. +*Beware*: Prisms languages have dependencies onto each other. You need to import the languages together with their dependencies in the +correct order. diff --git a/src/index.ts b/src/index.ts index a6db3a82..42b3a96f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,18 @@ import Prism, {Grammar} from 'prismjs' import loadLanguages from 'prismjs/components/' import MarkdownIt from 'markdown-it' -import {RenderRule} from 'markdown-it/lib/renderer' +import Renderer, {RenderRule} from 'markdown-it/lib/renderer' +import StateCore from 'markdown-it/lib/rules_core/state_core' +import Token from 'markdown-it/lib/token' + +const SPECIFIED_LANGUAGE_META_KEY = 'de.joshuagleitze.markdown-it-prism.specifiedLanguage' +type SelectedPrismLanguage = [string, Grammar | undefined] interface Options { + /** + * Whether to highlight inline code. Defaults to `false`. + */ + highlightInlineCode: boolean /** * Prism plugins to load. */ @@ -29,6 +38,7 @@ interface Options { } const DEFAULTS: Options = { + highlightInlineCode: false, plugins: [], init: () => { // do nothing by default @@ -108,7 +118,24 @@ function selectLanguage(options: Options, lang: string): [string, Grammar | unde * html-escaped. */ function highlight(markdownit: MarkdownIt, options: Options, text: string, lang: string): string { - const [langToUse, prismLang] = selectLanguage(options, lang) + return highlightWithSelectedLanguage(markdownit, options, text, selectLanguage(options, lang)) +} + +/** + * Highlights the provided text using Prism. + * + * @param markdownit + * The markdown-it instance. + * @param options + * The options that have been used to initialise the plugin. + * @param text + * The text to highlight. + * @param lang + * The selected Prism language to use for highlighting. + * @return If Prism knows the language that {@link selectLanguage} returns for `lang`, the `text` highlighted for that language. Otherwise, `text` + * html-escaped. + */ +function highlightWithSelectedLanguage(markdownit: MarkdownIt, options: Options, text: string, [langToUse, prismLang]: SelectedPrismLanguage): string { return prismLang ? Prism.highlight(text, prismLang, langToUse) : markdownit.utils.escapeHtml(text) } @@ -122,7 +149,43 @@ function highlight(markdownit: MarkdownIt, options: Options, text: string, lang: * @return the class to use for `lang`. */ function languageClass(markdownit: MarkdownIt, lang: string): string { - return markdownit.options.langPrefix + markdownit.utils.escapeHtml(lang) + return markdownit.options.langPrefix + lang +} + + +/** + * A {@link RuleCore} that searches for and extracts language specifications on inline code tokens. + */ +function inlineCodeLanguageRule(state: StateCore) { + for (const inlineToken of state.tokens) { + if (inlineToken.type === 'inline' && inlineToken.children !== null) { + for (const [index, token] of inlineToken.children.entries()) { + if (token.type === 'code_inline' && index + 1 < inlineToken.children.length) { + extractInlineCodeSpecifiedLanguage(token, inlineToken.children[index + 1]) + } + } + } + } +} + +/** + * Searches for a language specification after an inline code token (e.g. ``{language=cpp}). If present, extracts the language, sets + * it on `inlineCodeToken`’s meta, and removes the specification. + * + * @param inlineCodeToken + * The inline code token for which to extract the language. + * @param followingToken + * The token immediately following the `inlineCodeToken`. + */ +function extractInlineCodeSpecifiedLanguage(inlineCodeToken: Token, followingToken: Token) { + const languageSpecificationMatch = followingToken.content.match(/^\{((?:[^\s}]+\s)*)language=([^\s}]+)((?:\s[^\s}]+)*)}/) + if (languageSpecificationMatch !== null) { + inlineCodeToken.meta = {...inlineCodeToken.meta, [SPECIFIED_LANGUAGE_META_KEY]: languageSpecificationMatch[2]} + followingToken.content = followingToken.content.slice(languageSpecificationMatch[0].length) + if (languageSpecificationMatch[1] || languageSpecificationMatch[3]) { + followingToken.content = `{${languageSpecificationMatch[1] || ''}${(languageSpecificationMatch[3] || ' ').slice(1)}}${followingToken.content}` + } + } } /** @@ -133,7 +196,7 @@ function languageClass(markdownit: MarkdownIt, lang: string): string { * @param options * The options that have been used to initialise the plugin. * @param existingRule - * The currently configured render rule for fenced code blocks. + * The previously configured render rule for fenced code blocks. */ function applyCodeAttributes(markdownit: MarkdownIt, options: Options, existingRule: RenderRule): RenderRule { return (tokens, idx, renderOptions, env, self) => { @@ -146,7 +209,7 @@ function applyCodeAttributes(markdownit: MarkdownIt, options: Options, existingR } else { fenceToken.info = langToUse const existingResult = existingRule(tokens, idx, renderOptions, env, self) - const langClass = languageClass(markdownit, langToUse) + const langClass = languageClass(markdownit, markdownit.utils.escapeHtml(langToUse)) return existingResult.replace( /<((?:pre|code)[^>]*?)(?:\s+class="([^"]*)"([^>]*))?>/g, (match, tagStart, existingClasses?: string, tagEnd?) => @@ -157,6 +220,31 @@ function applyCodeAttributes(markdownit: MarkdownIt, options: Options, existingR } } +/** + * Renders inline code tokens by highlighting them with Prism. + * + * @param markdownit + * The markdown-it instance. + * @param options + * The options that have been used to initialise the plugin. + * @param existingRule + * The previously configured render rule for inline code. + */ +function renderInlineCode(markdownit: MarkdownIt, options: Options, existingRule: RenderRule): RenderRule { + return (tokens, idx, renderOptions, env, self) => { + const inlineCodeToken = tokens[idx] + const specifiedLanguage = inlineCodeToken.meta ? (inlineCodeToken.meta[SPECIFIED_LANGUAGE_META_KEY] || '') : '' + const [langToUse, prismLang] = selectLanguage(options, specifiedLanguage) + if (!langToUse) { + return existingRule(tokens, idx, renderOptions, env, self) + } else { + const highlighted = highlightWithSelectedLanguage(markdownit, options, inlineCodeToken.content, [langToUse, prismLang]) + inlineCodeToken.attrJoin('class', languageClass(markdownit, langToUse)) + return `${highlighted}` + } + } +} + /** * Checks whether an option represents a valid Prism language * @@ -176,6 +264,13 @@ function checkLanguageOption( } } +/** + * ‘the most basic rule to render a token’ (https://github.com/markdown-it/markdown-it/blob/master/docs/examples/renderer_rules.md) + */ +function renderFallback(tokens: Token[], idx: number, options: MarkdownIt.Options, env: unknown, self: Renderer): string { + return self.renderToken(tokens, idx, options) +} + /** * Initialisation function of the plugin. This function is not called directly by clients, but is rather provided * to MarkdownIt’s {@link MarkdownIt.use} function. @@ -199,5 +294,9 @@ export default function markdownItPrism(markdownit: MarkdownIt, useroptions: Opt // register ourselves as highlighter markdownit.options.highlight = (text, lang) => highlight(markdownit, options, text, lang) - markdownit.renderer.rules.fence = applyCodeAttributes(markdownit, options, markdownit.renderer.rules.fence || (() => '')) + markdownit.renderer.rules.fence = applyCodeAttributes(markdownit, options, markdownit.renderer.rules.fence || renderFallback) + if (options.highlightInlineCode) { + markdownit.core.ruler.after('inline', 'prism_inline_code_language', inlineCodeLanguageRule) + markdownit.renderer.rules.code_inline = renderInlineCode(markdownit, options, markdownit.renderer.rules.code_inline || renderFallback) + } } diff --git a/test/highlighting.test.ts b/test/highlighting.test.ts new file mode 100644 index 00000000..0aab152b --- /dev/null +++ b/test/highlighting.test.ts @@ -0,0 +1,113 @@ +import markdownit from 'markdown-it' + +import markdownItPrism from '../src' +import {read} from './util' + +const codeSectionTypeSettings = { + 'fenced': {}, + 'inline': {highlightInlineCode: true} +} + +describe('code highlighting', () => { + Object.entries(codeSectionTypeSettings).forEach(([codeSectionType, options]) => describe(`${codeSectionType} code`, () => { + + it('highlights code with a language specification using Prism', async () => { + expect(markdownit() + .use(markdownItPrism, options) + .render(await read(`input/${codeSectionType}/with-language.md`)) + ).toEqual(await read(`expected/${codeSectionType}/with-language.html`)) + }) + + it('does not add classes to code without language specification', async () => { + expect(markdownit() + .use(markdownItPrism, options) + .render(await read(`input/${codeSectionType}/without-language.md`)) + ).toEqual(await read(`expected/${codeSectionType}/without-language.html`)) + }) + + it('falls back to defaultLanguageForUnspecified if no language is specified', async () => { + expect(markdownit() + .use(markdownItPrism, { + defaultLanguageForUnspecified: 'java', + ...options + }) + .render(await read(`input/${codeSectionType}/without-language.md`)) + ).toEqual(await read(`expected/${codeSectionType}/with-language.html`)) + }) + + it('falls back to defaultLanguage if no language and no defaultLanguageForUnspecified is specified', async () => { + expect(markdownit() + .use(markdownItPrism, { + defaultLanguage: 'java', + ...options + }) + .render(await read(`input/${codeSectionType}/without-language.md`)) + ).toEqual(await read(`expected/${codeSectionType}/with-language.html`)) + }) + + it('does not add classes to indented code blocks', async () => { + expect(markdownit() + .use(markdownItPrism, options) + .render(await read('input/indented.md')) + ).toEqual(await read('expected/indented.html')) + }) + + it('adds classes even if the language is unknown', async () => { + expect(markdownit() + .use(markdownItPrism, options) + .render(await read(`input/${codeSectionType}/with-unknown-language.md`)) + ).toEqual(await read(`expected/${codeSectionType}/with-unknown-language.html`)) + }) + + it('escapes HTML in the language name', async () => { + expect(markdownit() + .use(markdownItPrism, options) + .render(await read(`input/${codeSectionType}/with-html-in-language.md`)) + ).toEqual(await read(`expected/${codeSectionType}/with-html-in-language.html`)) + }) + + it('falls back to defaultLanguageForUnknown if the specified language is unknown', async () => { + expect(markdownit() + .use(markdownItPrism, { + defaultLanguageForUnknown: 'java', + ...options + }) + .render(await read(`input/${codeSectionType}/with-unknown-language.md`)) + ).toEqual(await read(`expected/${codeSectionType}/with-language.html`)) + }) + + it('falls back to defaultLanguage if the specified language is unknown and no defaultLanguageForUnknown is specified', async () => { + expect(markdownit() + .use(markdownItPrism, { + defaultLanguage: 'java', + ...options + }) + .render(await read(`input/${codeSectionType}/with-unknown-language.md`)) + ).toEqual(await read(`expected/${codeSectionType}/with-language.html`)) + }) + + it('respects markdown-it’s langPrefix setting', async () => { + expect( + markdownit({ + langPrefix: 'test-', + }) + .use(markdownItPrism, options) + .render(await read(`input/${codeSectionType}/with-language.md`)) + ).toEqual(await read(`expected/${codeSectionType}/with-language-prefix.html`)) + }) + + it('is able to resolve C++ correctly', async () => { + expect(markdownit() + .use(markdownItPrism, options) + .render(await read(`input/${codeSectionType}/cpp.md`)) + ).toEqual(await read(`expected/${codeSectionType}/cpp.html`)) + }) + })) + + it('does not highlight inline code unless configured', async () => { + expect(markdownit() + .use(markdownItPrism, codeSectionTypeSettings.fenced) + .render(await read('input/inline/with-language.md')) + ).toEqual(await (read('expected/inline/not-highlighted.html'))) + }) +}) diff --git a/test/options.test.ts b/test/options.test.ts new file mode 100644 index 00000000..46f863b7 --- /dev/null +++ b/test/options.test.ts @@ -0,0 +1,41 @@ +import markdownit from 'markdown-it' +import markdownItPrism from '../src' + +describe('option handling', () => { + it('throws for unknown plugins', () => { + expect(() => markdownit() + .use(markdownItPrism, { + plugins: ['foo'] + })).toThrowError(/plugin/) + }) + + it('throws for unknown defaultLanguage', () => { + expect(() => markdownit() + .use(markdownItPrism, { + defaultLanguage: 'i-dont-exist' + })).toThrowError(/defaultLanguage.*i-dont-exist/) + }) + + it('throws for unknown defaultLanguageForUnknown', () => { + expect(() => markdownit() + .use(markdownItPrism, { + defaultLanguageForUnknown: 'i-dont-exist' + })).toThrowError(/defaultLanguageForUnknown.*i-dont-exist/) + }) + + it('throws for unknown defaultLanguageForUnspecified', () => { + expect(() => markdownit() + .use(markdownItPrism, { + defaultLanguageForUnspecified: 'i-dont-exist' + })).toThrowError(/defaultLanguageForUnspecified.*i-dont-exist/) + }) + + it('offers an init function for further initialisation', () => { + const initCallback = jest.fn(prism => { + expect(prism).toHaveProperty('plugins') + }) + markdownit() + .use(markdownItPrism, {init: initCallback}) + expect(initCallback).toHaveBeenCalled + }) +}) diff --git a/test/plugins.test.ts b/test/plugins.test.ts new file mode 100644 index 00000000..9d35ee71 --- /dev/null +++ b/test/plugins.test.ts @@ -0,0 +1,38 @@ +import markdownit from 'markdown-it' +// @ts-ignore markdown-it-attrs has no types, and it’s not worth the effort adding a *.d.ts file +import markdownItAttrs from 'markdown-it-attrs' +import markdownItPrism from '../src' +import {read} from './util' + +describe('plugin support', () => { + afterEach(() => jest.resetModules()) + + it('allows to use markdown-it-attrs (attrs loaded first)', async () => { + expect(markdownit() + .use(markdownItAttrs) + .use(markdownItPrism, {highlightInlineCode: true}) + .render(await read('input/all-with-attrs.md')) + ).toEqual(await read('expected/all-with-attrs.html')) + }) + + it('allows to use markdown-it-attrs (attrs loaded second)', async () => { + expect(markdownit() + .use(markdownItPrism, {highlightInlineCode: true}) + .use(markdownItAttrs) + .render(await read('input/all-with-attrs.md')) + ).toEqual(await read('expected/all-with-attrs.html')) + }) + + it('allows to use Prism plugins', async () => { + expect(markdownit() + .use(markdownItPrism, { + highlightInlineCode: true, + plugins: [ + 'highlight-keywords', + 'show-language' + ] + }) + .render(await read('input/all-with-language.md')) + ).toEqual(await read('expected/all-with-language-and-plugins.html')) + }) +}) diff --git a/test/test.ts b/test/test.ts deleted file mode 100644 index 30130e60..00000000 --- a/test/test.ts +++ /dev/null @@ -1,160 +0,0 @@ -import markdownit from 'markdown-it' -// @ts-ignore markdown-it-attrs has no types and it’s not worth the effort adding a *.d.ts file -import markdownItAttrs from 'markdown-it-attrs' -import markdownItPrism from '../src' -import fs from 'fs' - -const read = async (path: string) => (await fs.promises.readFile(`testdata/${path}`)).toString() - -describe('markdown-it-prism', () => { - - it('highlights fenced code blocks with a language specification using Prism', async () => { - expect(markdownit() - .use(markdownItPrism) - .render(await read('input/fenced-with-language.md')) - ).toEqual(await read('expected/fenced-with-language.html')) - }) - - it('throws for unknown plugins', () => { - expect(() => markdownit() - .use(markdownItPrism, { - plugins: ['foo'] - })).toThrowError(/plugin/) - }) - - it('throws for unknown defaultLanguage', () => { - expect(() => markdownit() - .use(markdownItPrism, { - defaultLanguage: 'i-dont-exist' - })).toThrowError(/defaultLanguage.*i-dont-exist/) - }) - - it('throws for unknown defaultLanguageForUnknown', () => { - expect(() => markdownit() - .use(markdownItPrism, { - defaultLanguageForUnknown: 'i-dont-exist' - })).toThrowError(/defaultLanguageForUnknown.*i-dont-exist/) - }) - - it('throws for unknown defaultLanguageForUnspecified', () => { - expect(() => markdownit() - .use(markdownItPrism, { - defaultLanguageForUnspecified: 'i-dont-exist' - })).toThrowError(/defaultLanguageForUnspecified.*i-dont-exist/) - }) - - it('offers an init function for further initialisation', () => { - const initCallback = jest.fn(prism => { - expect(prism).toHaveProperty('plugins') - }) - markdownit() - .use(markdownItPrism, {init: initCallback}) - expect(initCallback).toHaveBeenCalled - }) - - it('does not add classes to fenced code blocks without language specification', async () => { - expect(markdownit() - .use(markdownItPrism) - .render(await read('input/fenced-without-language.md')) - ).toEqual(await read('expected/fenced-without-language.html')) - }) - - it('falls back to defaultLanguageForUnspecified if no language is specified', async () => { - expect(markdownit() - .use(markdownItPrism, { - defaultLanguageForUnspecified: 'java' - }) - .render(await read('input/fenced-without-language.md')) - ).toEqual(await read('expected/fenced-with-language.html')) - }) - - it('falls back to defaultLanguage if no language and no defaultLanguageForUnspecified is specified', async () => { - expect(markdownit() - .use(markdownItPrism, { - defaultLanguage: 'java' - }) - .render(await read('input/fenced-without-language.md')) - ).toEqual(await read('expected/fenced-with-language.html')) - }) - - it('does not add classes to indented code blocks', async () => { - expect(markdownit() - .use(markdownItPrism) - .render(await read('input/indented.md')) - ).toEqual(await read('expected/indented.html')) - }) - - it('adds classes even if the language is unknown', async () => { - expect(markdownit() - .use(markdownItPrism) - .render(await read('input/fenced-with-unknown-language.md')) - ).toEqual(await read('expected/fenced-with-unknown-language.html')) - }) - - it('escapes HTML in the language name', async () => { - expect(markdownit() - .use(markdownItPrism) - .render(await read('input/fenced-with-html-in-language.md')) - ).toEqual(await read('expected/fenced-with-html-in-language.html')) - }) - - it('falls back to defaultLanguageForUnknown if the specified language is unknown', async () => { - expect(markdownit() - .use(markdownItPrism, { - defaultLanguageForUnknown: 'java' - }) - .render(await read('input/fenced-with-unknown-language.md')) - ).toEqual(await read('expected/fenced-with-language.html')) - }) - - it('falls back to defaultLanguage if the specified language is unknown and no defaultLanguageForUnknown is specified', async () => { - expect(markdownit() - .use(markdownItPrism, { - defaultLanguage: 'java' - }) - .render(await read('input/fenced-with-unknown-language.md')) - ).toEqual(await read('expected/fenced-with-language.html')) - }) - - it('respects markdown-it’s langPrefix setting', async () => { - expect( - markdownit({ - langPrefix: 'test-' - }) - .use(markdownItPrism) - .render(await read('input/fenced-with-language.md')) - ).toEqual(await read('expected/fenced-with-language-prefix.html')) - }) - - it('is able to resolve C++ correctly', async () => { - expect(markdownit() - .use(markdownItPrism) - .render(await read('input/cpp.md')) - ).toEqual(await read('expected/cpp.html')) - }) - - describe('plugin support', () => { - - afterEach(() => jest.resetModules()) - - it('allows to use markdown-it-attrs', async () => { - expect(markdownit() - .use(markdownItPrism) - .use(markdownItAttrs) - .render(await read('input/with-attrs.md')) - ).toEqual(await read('expected/fenced-with-attrs.html')) - }) - - it('allows to use Prism plugins', async () => { - expect(markdownit() - .use(markdownItPrism, { - plugins: [ - 'highlight-keywords', - 'show-language' - ] - }) - .render(await read('input/fenced-with-language.md')) - ).toEqual(await read('expected/fenced-with-language-plugins.html')) - }) - }) -}) diff --git a/test/util.ts b/test/util.ts new file mode 100644 index 00000000..705a94f7 --- /dev/null +++ b/test/util.ts @@ -0,0 +1,5 @@ +import fs from 'fs' + +export async function read(path: string) { + return (await fs.promises.readFile(`testdata/${path}`)).toString() +} diff --git a/testdata/expected/all-with-attrs.html b/testdata/expected/all-with-attrs.html new file mode 100644 index 00000000..fa0215a9 --- /dev/null +++ b/testdata/expected/all-with-attrs.html @@ -0,0 +1,11 @@ +

Test

+

This is a fenced code block:

+
public class Foo() {
+	public Foo(String bar) {
+		System.out.println(bar);
+	}
+}
+
+

Please see: class Demo { };, nice, isn’t it?

+

Also see: class Demo { };!

+

And, finally: class Demo { };. Awesome!

diff --git a/testdata/expected/fenced-with-language-plugins.html b/testdata/expected/all-with-language-and-plugins.html similarity index 68% rename from testdata/expected/fenced-with-language-plugins.html rename to testdata/expected/all-with-language-and-plugins.html index 4fe68e52..7818753d 100644 --- a/testdata/expected/fenced-with-language-plugins.html +++ b/testdata/expected/all-with-language-and-plugins.html @@ -1,8 +1,9 @@

Test

This is a fenced code block:

public class Foo() {
-	public Foo(bar) {
+	public Foo(String bar) {
 		System.out.println(bar);
 	}
 }
 
+

And here’s inline class Demo { };

diff --git a/testdata/expected/fenced-with-attrs.html b/testdata/expected/fenced-with-attrs.html deleted file mode 100644 index ec1594d3..00000000 --- a/testdata/expected/fenced-with-attrs.html +++ /dev/null @@ -1,8 +0,0 @@ -

Test

-

This is a fenced code block:

-
public class Foo() {
-	public Foo(bar) {
-		System.out.println(bar);
-	}
-}
-
diff --git a/testdata/expected/cpp.html b/testdata/expected/fenced/cpp.html similarity index 100% rename from testdata/expected/cpp.html rename to testdata/expected/fenced/cpp.html diff --git a/testdata/expected/fenced-with-html-in-language.html b/testdata/expected/fenced/with-html-in-language.html similarity index 92% rename from testdata/expected/fenced-with-html-in-language.html rename to testdata/expected/fenced/with-html-in-language.html index 8377baff..1685e531 100644 --- a/testdata/expected/fenced-with-html-in-language.html +++ b/testdata/expected/fenced/with-html-in-language.html @@ -1,7 +1,7 @@

Test

This is a fenced code block:

public class Foo() {
-	public Foo(bar) {
+	public Foo(String bar) {
 		System.out.println(bar);
 	}
 }
diff --git a/testdata/expected/fenced-with-language-prefix.html b/testdata/expected/fenced/with-language-prefix.html
similarity index 80%
rename from testdata/expected/fenced-with-language-prefix.html
rename to testdata/expected/fenced/with-language-prefix.html
index 534e0e8a..8c3dfd45 100644
--- a/testdata/expected/fenced-with-language-prefix.html
+++ b/testdata/expected/fenced/with-language-prefix.html
@@ -1,7 +1,7 @@
 

Test

This is a fenced code block:

public class Foo() {
-	public Foo(bar) {
+	public Foo(String bar) {
 		System.out.println(bar);
 	}
 }
diff --git a/testdata/expected/fenced-with-language.html b/testdata/expected/fenced/with-language.html
similarity index 80%
rename from testdata/expected/fenced-with-language.html
rename to testdata/expected/fenced/with-language.html
index c2c451e4..bd8b94ac 100644
--- a/testdata/expected/fenced-with-language.html
+++ b/testdata/expected/fenced/with-language.html
@@ -1,7 +1,7 @@
 

Test

This is a fenced code block:

public class Foo() {
-	public Foo(bar) {
+	public Foo(String bar) {
 		System.out.println(bar);
 	}
 }
diff --git a/testdata/expected/fenced-with-unknown-language.html b/testdata/expected/fenced/with-unknown-language.html
similarity index 88%
rename from testdata/expected/fenced-with-unknown-language.html
rename to testdata/expected/fenced/with-unknown-language.html
index 2043c85a..d957b3ca 100644
--- a/testdata/expected/fenced-with-unknown-language.html
+++ b/testdata/expected/fenced/with-unknown-language.html
@@ -1,7 +1,7 @@
 

Test

This is a fenced code block:

public class Foo() {
-	public Foo(bar) {
+	public Foo(String bar) {
 		System.out.println(bar);
 	}
 }
diff --git a/testdata/expected/fenced-without-language.html b/testdata/expected/fenced/without-language.html
similarity index 83%
rename from testdata/expected/fenced-without-language.html
rename to testdata/expected/fenced/without-language.html
index b1c58b84..0fb5fb2f 100644
--- a/testdata/expected/fenced-without-language.html
+++ b/testdata/expected/fenced/without-language.html
@@ -1,7 +1,7 @@
 

Test

This is a fenced code block:

public class Foo() {
-	public Foo(bar) {
+	public Foo(String bar) {
 		System.out.println(bar);
 	}
 }
diff --git a/testdata/expected/indented.html b/testdata/expected/indented.html
index af779848..670b183d 100644
--- a/testdata/expected/indented.html
+++ b/testdata/expected/indented.html
@@ -1,7 +1,7 @@
 

Test

This is an indented code block:

public class Foo() {
-	public Foo(bar) {
+	public Foo(String bar) {
 		System.out.println(bar);
 	}
 }
diff --git a/testdata/expected/inline/cpp.html b/testdata/expected/inline/cpp.html
new file mode 100644
index 00000000..886539d1
--- /dev/null
+++ b/testdata/expected/inline/cpp.html
@@ -0,0 +1 @@
+

class Demo { };

diff --git a/testdata/expected/inline/not-highlighted.html b/testdata/expected/inline/not-highlighted.html new file mode 100644 index 00000000..5f53ee1d --- /dev/null +++ b/testdata/expected/inline/not-highlighted.html @@ -0,0 +1 @@ +

System.out.println("bar"){language=java}

diff --git a/testdata/expected/inline/with-html-in-language.html b/testdata/expected/inline/with-html-in-language.html new file mode 100644 index 00000000..99f87964 --- /dev/null +++ b/testdata/expected/inline/with-html-in-language.html @@ -0,0 +1 @@ +

System.out.println("bar")

diff --git a/testdata/expected/inline/with-language-prefix.html b/testdata/expected/inline/with-language-prefix.html new file mode 100644 index 00000000..990086e1 --- /dev/null +++ b/testdata/expected/inline/with-language-prefix.html @@ -0,0 +1 @@ +

System.out.println("bar")

diff --git a/testdata/expected/inline/with-language.html b/testdata/expected/inline/with-language.html new file mode 100644 index 00000000..fcfa6cf9 --- /dev/null +++ b/testdata/expected/inline/with-language.html @@ -0,0 +1 @@ +

System.out.println("bar")

diff --git a/testdata/expected/inline/with-unknown-language.html b/testdata/expected/inline/with-unknown-language.html new file mode 100644 index 00000000..1ace661a --- /dev/null +++ b/testdata/expected/inline/with-unknown-language.html @@ -0,0 +1 @@ +

System.out.println("bar")

diff --git a/testdata/expected/inline/without-language.html b/testdata/expected/inline/without-language.html new file mode 100644 index 00000000..4955f6a0 --- /dev/null +++ b/testdata/expected/inline/without-language.html @@ -0,0 +1 @@ +

System.out.println("bar")

diff --git a/testdata/input/all-with-attrs.md b/testdata/input/all-with-attrs.md new file mode 100644 index 00000000..eaf76525 --- /dev/null +++ b/testdata/input/all-with-attrs.md @@ -0,0 +1,17 @@ +# Test + +This is a fenced code block: + +```java {.classname data-custom=value} +public class Foo() { + public Foo(String bar) { + System.out.println(bar); + } +} +``` + +Please see: `class Demo { };`{language=cpp .red}, nice, isn’t it? + +Also see: `class Demo { };`{.red language=cpp}! + +And, finally: `class Demo { };`{foo=bar .red language=cpp lang=en #awesome}. Awesome! diff --git a/testdata/input/all-with-language.md b/testdata/input/all-with-language.md new file mode 100644 index 00000000..ab935c5b --- /dev/null +++ b/testdata/input/all-with-language.md @@ -0,0 +1,13 @@ +# Test + +This is a fenced code block: + +```java +public class Foo() { + public Foo(String bar) { + System.out.println(bar); + } +} +``` + +And here’s inline `class Demo { };`{language=cpp} diff --git a/testdata/input/cpp.md b/testdata/input/fenced/cpp.md similarity index 100% rename from testdata/input/cpp.md rename to testdata/input/fenced/cpp.md diff --git a/testdata/input/fenced-with-html-in-language.md b/testdata/input/fenced/with-html-in-language.md similarity index 85% rename from testdata/input/fenced-with-html-in-language.md rename to testdata/input/fenced/with-html-in-language.md index 9c645a36..589e8e6d 100644 --- a/testdata/input/fenced-with-html-in-language.md +++ b/testdata/input/fenced/with-html-in-language.md @@ -4,7 +4,7 @@ This is a fenced code block: ```">