{
+ let inputString = await utility.readFile(this.filePath, {encoding: 'utf-8'})
+ let {data:config} = this.processFrontMatter(inputString, false)
+
+ if (inputString.startsWith('---\n')) {
+ const end = inputString.indexOf('---\n', 4)
+ inputString = inputString.slice(end+4)
+ }
+
+ config = config['markdown'] || {}
+ if (!config['image_dir']) {
+ config['image_dir'] = this.config.imageFolderPath
+ }
+
+ if (!config['path']) {
+ if (this.filePath.match(/\.src\./)) {
+ config['path'] = this.filePath.replace(/\.src\./, '.')
+ } else {
+ config['path'] = this.filePath.replace(new RegExp(path.extname(this.filePath)), '_'+path.extname(this.filePath))
+ }
+ config['path'] = path.basename(config['path'])
+ }
+
+ if (config['front_matter']) {
+ inputString = matter.stringify(inputString, config['front-matter'])
+ }
+
+ return await markdownConvert(inputString, {
+ projectDirectoryPath: this.projectDirectoryPath,
+ fileDirectoryPath: this.fileDirectoryPath,
+ protocolsWhiteListRegExp: this.protocolsWhiteListRegExp,
+ filesCache: this.filesCache,
+ mathInlineDelimiters: this.config.mathInlineDelimiters,
+ mathBlockDelimiters: this.config.mathBlockDelimiters,
+ codeChunksData: this.codeChunksData,
+ graphsCache: this.graphsCache
+ }, config)
+ }
+
/**
*
* @param filePath
@@ -920,26 +1005,57 @@ export class MarkdownEngine {
if (!codeChunkData) return ''
if (codeChunkData.running) return ''
+ let code = codeChunkData.code
+ let cc = codeChunkData
+ while (cc.options['continue']) {
+ let id = cc.options['continue']
+ if (id === true) {
+ id = cc.prev
+ }
+ cc = this.codeChunksData[id]
+ if (!cc) break
+ code = cc.code + code
+ }
+
codeChunkData.running = true
- let result = await CodeChunkAPI.run(codeChunkData.code, this.fileDirectoryPath, codeChunkData.options)
-
- const outputFormat = codeChunkData.options['output'] || 'text'
- if (outputFormat === 'html') {
- result = result
- } else if (outputFormat === 'png') {
- const base64 = new Buffer(result).toString('base64')
- result = ``
- } else if (outputFormat === 'markdown') {
- const {html} = await this.parseMD(result, {useRelativeImagePath:true, isForPreview:false, hideFrontMatter: true} )
- result = html
- } else if (outputFormat === 'none') {
- result = ''
- } else {
- result = `${result}
`
+ let result
+ try {
+ const options = codeChunkData.options
+ if (options['cmd'] === 'toc') { // toc code chunk. <= this is a special code chunk.
+ const tocObject = toc(this.headings, {ordered: options['orderedList'], depthFrom: options['depthFrom'], depthTo: options['depthTo'], tab: options['tab'] || '\t'})
+ result = tocObject.content
+ } else {
+ result = await CodeChunkAPI.run(code, this.fileDirectoryPath, codeChunkData.options)
+ }
+ codeChunkData.plainResult = result
+
+ if (codeChunkData.options['modify_source'] && ('code_chunk_offset' in codeChunkData.options)) {
+ codeChunkData.result = ''
+ return MarkdownEngine.modifySource(codeChunkData, result, this.filePath)
+ }
+
+ const outputFormat = codeChunkData.options['output'] || 'text'
+ if (!result) { // do nothing
+ result = ''
+ } else if (outputFormat === 'html') {
+ result = result
+ } else if (outputFormat === 'png') {
+ const base64 = new Buffer(result).toString('base64')
+ result = ``
+ } else if (outputFormat === 'markdown') {
+ const {html} = await this.parseMD(result, {useRelativeFilePath:true, isForPreview:false, hideFrontMatter: true} )
+ result = html
+ } else if (outputFormat === 'none') {
+ result = ''
+ } else {
+ result = `${result}
`
+ }
+ } catch(error) {
+ result = `${error}
`
}
- codeChunkData.running = false
codeChunkData.result = result // save result.
+ codeChunkData.running = false
return result
}
@@ -950,7 +1066,23 @@ export class MarkdownEngine {
}
return await Promise.all(asyncFunctions)
}
-
+ /**
+ * Add line numbers to code block element
+ * @param
+ * @param code
+ */
+ private addLineNumbersIfNecessary($preElement, code:string):void {
+ if ($preElement.hasClass('line-numbers')) {
+ const match = code.match(/\n(?!$)/g)
+ const linesNum = match ? (match.length + 1) : 1
+ let lines = ''
+ for (let i = 0; i < linesNum; i++) {
+ lines += ''
+ }
+ $preElement.append(`${lines}`)
+ }
+ }
+
/**
*
* @param preElement the cheerio element
@@ -959,7 +1091,10 @@ export class MarkdownEngine {
*/
private async renderCodeBlock($, $preElement, code, parameters,
{ graphsCache,
- codeChunksArray}:{graphsCache:object, codeChunksArray:CodeChunkData[]}) {
+ codeChunksArray,
+ isForPreview,
+ triggeredBySave }:{graphsCache:object, codeChunksArray:CodeChunkData[], isForPreview:boolean, triggeredBySave:boolean}) {
+
let match, lang, optionsStr:string, options:object
if (match = parameters.match(/\s*([^\s]+)\s+\{(.+?)\}/)) {
lang = match[1]
@@ -979,23 +1114,27 @@ export class MarkdownEngine {
options = {}
}
- function renderPlainCodeBlock() {
+ const renderPlainCodeBlock = ()=> {
try {
if (!Prism) {
- Prism = require(path.resolve(getExtensionDirectoryPath(), './dependencies/prism/prism.js'))
+ Prism = require(path.resolve(extensionDirectoryPath, './dependencies/prism/prism.js'))
}
const html = Prism.highlight(code, Prism.languages[scopeForLanguageName(lang)])
$preElement.html(html)
} catch(e) {
// do nothing
}
+ if (options['class']) {
+ $preElement.addClass(options['class'])
+ this.addLineNumbersIfNecessary($preElement, code)
+ }
}
const codeBlockOnly = options['code_block']
if (codeBlockOnly) {
renderPlainCodeBlock()
} else if (lang.match(/^(puml|plantuml)$/)) { // PlantUML
- const checksum = md5(code)
+ const checksum = md5(optionsStr + code)
let svg:string = this.graphsCache[checksum]
if (!svg) {
svg = await plantumlAPI.render(code, this.fileDirectoryPath)
@@ -1004,7 +1143,7 @@ export class MarkdownEngine {
graphsCache[checksum] = svg // store to new cache
} else if (lang.match(/^mermaid$/)) { // mermaid
- const checksum = md5(code)
+ const checksum = md5(optionsStr + code)
let svg:string = this.graphsCache[checksum]
if (!svg) {
$preElement.replaceWith(`${code}
`)
@@ -1013,21 +1152,22 @@ export class MarkdownEngine {
graphsCache[checksum] = svg // store to new cache
}
} else if (lang.match(/^(dot|viz)$/)) { // GraphViz
- const checksum = md5(code)
+ const checksum = md5(optionsStr + code)
let svg = this.graphsCache[checksum]
- if (!svg) {
- if (!viz) viz = require(path.resolve(extensionDirectoryPath, './dependencies/viz/viz.js'))
-
+ if (!svg) {
try {
let engine = options['engine'] || "dot"
- svg = viz(code, {engine})
+ svg = Viz(code, {engine})
+
+ $preElement.replaceWith(`${svg}
`)
+ graphsCache[checksum] = svg // store to new cache
} catch(e) {
- $preElement.replaceWith(`${e.toString()}
`)
+ $preElement.replaceWith(`${e.toString()}
`)
}
- }
-
- $preElement.replaceWith(`${svg}
`)
- graphsCache[checksum] = svg // store to new cache
+ } else {
+ $preElement.replaceWith(`${svg}
`)
+ graphsCache[checksum] = svg // store to new cache
+ }
} else if (options['cmd']) {
const $el = $("") // create code chunk
if (!options['id']) {
@@ -1039,21 +1179,28 @@ export class MarkdownEngine {
}
$el.attr({
- 'data-id': options['id']
+ 'data-id': options['id'],
+ 'data-cmd': options['cmd'],
+ 'data-code': options['cmd'] === 'javascript' ? code : ''
})
let highlightedBlock = ''
if (!options['hide']) {
try {
if (!Prism) {
- Prism = require(path.resolve(getExtensionDirectoryPath(), './dependencies/prism/prism.js'))
+ Prism = require(path.resolve(extensionDirectoryPath, './dependencies/prism/prism.js'))
}
- highlightedBlock = `${Prism.highlight(code, Prism.languages[scopeForLanguageName(lang)])}
`
+ highlightedBlock = `${Prism.highlight(code, Prism.languages[scopeForLanguageName(lang)])}
`
} catch(e) {
// do nothing
- highlightedBlock = `${code}
`
+ highlightedBlock = `${code}
`
}
+
+ const $highlightedBlock = $(highlightedBlock)
+ this.addLineNumbersIfNecessary($highlightedBlock, code)
+ highlightedBlock = $.html($highlightedBlock)
}
+
/*
if (!options['id']) { // id is required for code chunk
highlightedBlock = `'id' is required for code chunk
`
@@ -1067,8 +1214,10 @@ export class MarkdownEngine {
code,
options: options,
result: '',
+ plainResult: '',
running: false,
- prev: previousCodeChunkDataId
+ prev: previousCodeChunkDataId,
+ next: null
}
this.codeChunksData[options['id']] = codeChunkData
} else {
@@ -1076,14 +1225,34 @@ export class MarkdownEngine {
codeChunkData.options = options
codeChunkData.prev = previousCodeChunkDataId
}
- codeChunksArray.push(codeChunkData)
+ if (previousCodeChunkDataId && this.codeChunksData[previousCodeChunkDataId])
+ this.codeChunksData[previousCodeChunkDataId].next = options['id']
+
+ codeChunksArray.push(codeChunkData) // this line has to be put above the `if` statement.
+
+ if (triggeredBySave && options['run_on_save']) {
+ await this.runCodeChunk(options['id'])
+ }
+
+ let result = codeChunkData.result
+ // element option
+ if (!result && codeChunkData.options['element']) {
+ result = codeChunkData.options['element']
+ codeChunkData.result = result
+ }
if (codeChunkData.running) {
$el.addClass('running')
}
const statusDiv = `running...
`
const buttonGroup = ''
- const outputDiv = `${codeChunkData.result}
`
+ let outputDiv = `${result}
`
+
+ // check javascript code chunk
+ if (!isForPreview && options['cmd'] === 'javascript') {
+ outputDiv += ``
+ result = codeChunkData.options['element'] || ''
+ }
$el.append(highlightedBlock)
$el.append(buttonGroup)
@@ -1102,19 +1271,6 @@ export class MarkdownEngine {
*/
private async resolveImagePathAndCodeBlock(html, options:MarkdownEngineRenderOption) {
let $ = cheerio.load(html, {xmlMode:true})
-
- // resolve image paths
- $('img, a').each((i, imgElement)=> {
- let srcTag = 'src'
- if (imgElement.name === 'a')
- srcTag = 'href'
-
- const img = $(imgElement)
- const src = img.attr(srcTag)
-
- img.attr(srcTag, this.resolveFilePath(src, options.useRelativeImagePath))
- })
-
// new caches
// which will be set when this.renderCodeBlocks is called
@@ -1143,14 +1299,31 @@ export class MarkdownEngine {
$preElement.attr('class', 'language-text')
}
- asyncFunctions.push(this.renderCodeBlock($, $preElement, code, lang, {graphsCache: newGraphsCache, codeChunksArray}))
+ asyncFunctions.push(this.renderCodeBlock($, $preElement, code, lang,
+ {graphsCache: newGraphsCache, codeChunksArray, isForPreview:options.isForPreview, triggeredBySave: options.triggeredBySave}))
})
await Promise.all(asyncFunctions)
+
+ // resolve image paths
+ $('img, a').each((i, imgElement)=> {
+ let srcTag = 'src'
+ if (imgElement.name === 'a')
+ srcTag = 'href'
+
+ const img = $(imgElement)
+ const src = img.attr(srcTag)
+
+ img.attr(srcTag, this.resolveFilePath(src, options.useRelativeFilePath))
+ })
+
// reset caches
// the line below actually has problem.
- this.graphsCache = newGraphsCache
+ if (options.isForPreview) {
+ this.graphsCache = newGraphsCache
+ }
+
return $.html()
}
@@ -1326,7 +1499,7 @@ export class MarkdownEngine {
`
}
- private parseSlidesForExport(html:string, slideConfigs:Array