diff --git a/src/editor/contentState/index.js b/src/editor/contentState/index.js index 9952ad17..bcfc7458 100644 --- a/src/editor/contentState/index.js +++ b/src/editor/contentState/index.js @@ -75,6 +75,13 @@ class ContentState { // getBlocks getBlocks () { + let key + let cm + for ([ key, cm ] of this.codeBlocks.entries()) { + const value = cm.getValue() + const block = this.getBlock(key) + if (block) block.text = value + } return this.blocks } diff --git a/src/editor/emojis/index.js b/src/editor/emojis/index.js index 1f25ca35..80bc2c13 100644 --- a/src/editor/emojis/index.js +++ b/src/editor/emojis/index.js @@ -31,7 +31,7 @@ export const setInlineEmoji = (node, emoji, selection) => { } class Emoji { - constructor (event) { + constructor () { this.cache = new Map() } diff --git a/src/editor/index.js b/src/editor/index.js index f93ef445..d552414f 100644 --- a/src/editor/index.js +++ b/src/editor/index.js @@ -8,14 +8,16 @@ import { checkEditLanguage, replaceLanguage } from './codeMirror/language' import Emoji, { checkEditEmoji, setInlineEmoji } from './emojis' import floatBox from './floatBox' import { findNearestParagraph, operateClassName } from './utils/domManipulate' +import ExportMarkdown from './utils/exportMarkdown' +import fs from 'fs' class Aganippe { constructor (container, options) { this.container = container this.eventCenter = eventCenter this.floatBox = floatBox - this.contentState = new ContentState(this.floatBox) - this.emoji = new Emoji(this.eventCenter) // emoji instance: has search(text) clear() methods. + this.contentState = new ContentState() + this.emoji = new Emoji() // emoji instance: has search(text) clear() methods. this.init() } @@ -39,6 +41,15 @@ class Aganippe { this.contentState.history.undo() }) + eventCenter.bind('command+s', event => { + const blocks = this.contentState.getBlocks() + const markdown = new ExportMarkdown(blocks).generate() + console.log(blocks) + fs.writeFile('./src/editor/output.md', markdown, 'utf-8', (err, data) => { + if (err) console.log(err) + }) + }) + // if you dont click the keyboard after 1 second, the garbageCollection will run. eventCenter.attachDOMEvent(container, 'keydown', debounce(() => this.contentState.garbageCollection(), 1000)) diff --git a/src/editor/output.md b/src/editor/output.md new file mode 100644 index 00000000..88e9d507 --- /dev/null +++ b/src/editor/output.md @@ -0,0 +1,5 @@ +> hello +> +- > hello +> +- > owowow diff --git a/src/editor/parser/StateRender.js b/src/editor/parser/StateRender.js index 04db79cc..fbfd7538 100644 --- a/src/editor/parser/StateRender.js +++ b/src/editor/parser/StateRender.js @@ -56,7 +56,7 @@ class StateRender { if (block.children.length) { return h(blockSelector, block.children.map(child => renderBlock(child))) } else { - const children = block.text + let children = block.text ? tokenizer(block.text).reduce((acc, token) => { const chunk = this[token.type](h, cursor, block, token) return Array.isArray(chunk) ? [...acc, ...chunk] : [...acc, chunk] @@ -74,6 +74,7 @@ class StateRender { if (block.type === 'pre') { if (block.lang) Object.assign(data.dataset, { lang: block.lang }) blockSelector += `.${CLASS_OR_ID['AG_CODE_BLOCK']}` + children = '' } if (block.temp) { diff --git a/src/editor/utils/exportMarkdown.js b/src/editor/utils/exportMarkdown.js new file mode 100644 index 00000000..bf8e8b71 --- /dev/null +++ b/src/editor/utils/exportMarkdown.js @@ -0,0 +1,148 @@ +const LINE_BREAKS = /\n/ + +class ExportMarkdown { + constructor (blocks) { + this.blocks = blocks + this.listType = [] // 'ul' or 'ol' + this.blockquoteType = [] + } + + generate () { + this.addDepth2Block(this.blocks, 0) + return this.translateBlocks2Markdown(this.blocks, 0) + } + + translateBlocks2Markdown (blocks, inLen) { + const result = [] + const len = blocks.length + const indent = ' '.repeat(inLen) + let i + for (i = 0; i < len; i++) { + const block = blocks[i] + switch (block.type) { + case 'p': + this.insertLineBreak(result, indent) + result.push(this.normalizeParagraphText(block, indent)) + break + + case 'h1': + case 'h2': + case 'h3': + case 'h4': + case 'h5': + case 'h6': + this.insertLineBreak(result, indent) + result.push(this.normalizeHeaderText(block, indent)) + break + + case 'li': + this.insertLineBreak(result, indent) + result.push(this.normalizeListItem(block, indent)) + break + + case 'ul': + this.insertLineBreak(result, indent) + this.listType.push({ type: 'ul' }) + result.push(this.normalizeList(block, indent)) + this.listType.pop() + break + + case 'ol': + this.insertLineBreak(result, indent) + const listCount = block.start !== undefined ? block.start : 1 + this.listType.push({ type: 'ol', listCount }) + result.push(this.normalizeList(block, indent)) + this.listType.pop() + break + + case 'pre': + this.insertLineBreak(result, indent) + result.push(this.normalizeCodeBlock(block, indent)) + break + + case 'blockquote': + this.insertLineBreak(result, indent) + this.blockquoteType.push({ type: 'blockquote' }) + result.push(this.normalizeBlockquote(block, indent)) + this.blockquoteType.pop() + break + } + } + console.log(result) + return result.join('') + } + + insertLineBreak (result, indent) { + if (result.length > 0) { + const depth = this.blockquoteType.length + const blockquotePrefix = depth ? `${'>'.repeat(depth)} ` : '' + result.push(blockquotePrefix ? `${indent}${blockquotePrefix}\n` : '\n') + } + } + + insertBlockquotePrefix (text, indent) { + const depth = this.blockquoteType.length + const blockquotePrefix = depth ? `${'> '.repeat(depth)}` : '' + return `${indent}${blockquotePrefix}${text}\n` + } + + normalizeHeaderText (block, indent) { + const match = block.text.match(/(#{1,6})(.*)/) + const text = `${match[1]} ${match[2].trim()}` + return this.insertBlockquotePrefix(text, indent) + } + + normalizeParagraphText (block, indent) { + return this.insertBlockquotePrefix(block.text, indent) + } + + normalizeBlockquote (block, indent) { + const { children } = block + return this.translateBlocks2Markdown(children, indent.length) + } + + normalizeCodeBlock (block, indent) { + const result = [] + const textList = block.text.split(LINE_BREAKS) + result.push(this.insertBlockquotePrefix(block.lang ? '```' + block.lang : '```', indent)) + + textList.forEach(text => { + result.push(this.insertBlockquotePrefix(text, indent)) + }) + result.push(this.insertBlockquotePrefix('```', indent)) + return result.join('') + } + + normalizeList (block, indent) { + const { children } = block + return this.translateBlocks2Markdown(children, indent.length) + } + + normalizeListItem (block, indent) { + const result = [] + const listInfo = this.listType[this.listType.length - 1] + const itemMarker = listInfo.type === 'ul' ? '- ' : `${listInfo.listCount++}. ` + const { children } = block + + result.push(`${indent}${itemMarker}`) + result.push(this.translateBlocks2Markdown(children, indent.length + itemMarker.length).trimLeft()) + + return result.join('') + } + + addDepth2Block (blocks, initDepth) { + const len = blocks.length + let i + + for (i = 0; i < len; i++) { + const block = blocks[i] + block.depth = initDepth + const { children } = block + if (children.length) { + this.addDepth2Block(children, initDepth + 1) + } + } + } +} + +export default ExportMarkdown diff --git a/src/renderer/components/Editor.vue b/src/renderer/components/Editor.vue index 00dde1e3..9228a9dc 100644 --- a/src/renderer/components/Editor.vue +++ b/src/renderer/components/Editor.vue @@ -1,6 +1,6 @@