diff --git a/docs/PREFERENCES.md b/docs/PREFERENCES.md index 3b7ced305..804d942c8 100644 --- a/docs/PREFERENCES.md +++ b/docs/PREFERENCES.md @@ -44,6 +44,8 @@ Preferences can be controlled and modified in the settings window or via the `pr | preferHeadingStyle | String | `atx` | The preferred heading style in Mark Text, optional value `atx` `setext`, [more info](https://spec.commonmark.org/0.29/#atx-headings) | | tabSize | Number | 4 | The number of spaces a tab is equal to | | listIndentation | String | 1 | The list indentation of sub list items or paragraphs, optional value `dfm`, `tab` or number 1~4 | +| frontmatterType | String | `-` | The frontmatter type: `-` (YAML), `+` (TOML), `;` (JSON) or `{` (JSON) | + #### View diff --git a/src/main/preferences/schema.json b/src/main/preferences/schema.json index 53d895b61..81e601278 100644 --- a/src/main/preferences/schema.json +++ b/src/main/preferences/schema.json @@ -165,6 +165,15 @@ 4 ] }, + "frontmatterType": { + "description": "Markdown--The frontmatter type", + "enum": [ + "-", + "+", + ";", + "{" + ] + }, "theme": { "description": "Theme--Select the theme used in Mark Text", "type": "string" diff --git a/src/muya/lib/config/index.js b/src/muya/lib/config/index.js index e0ff7db02..13099e8bb 100644 --- a/src/muya/lib/config/index.js +++ b/src/muya/lib/config/index.js @@ -250,6 +250,7 @@ export const MUYA_DEFAULT_OPTION = { tabSize: 4, // bullet/list marker width + listIndentation, tab or Daring Fireball Markdown (4 spaces) --> list indentation listIndentation: 1, + frontmatterType: '-', sequenceTheme: 'hand', // hand or simple mermaidTheme: 'default', // dark / forest / default vegaTheme: 'latimes', // excel / ggplot2 / quartz / vox / fivethirtyeight / dark / latimes diff --git a/src/muya/lib/contentState/paragraphCtrl.js b/src/muya/lib/contentState/paragraphCtrl.js index 38bdcb7e3..c1687887c 100644 --- a/src/muya/lib/contentState/paragraphCtrl.js +++ b/src/muya/lib/contentState/paragraphCtrl.js @@ -68,10 +68,33 @@ const paragraphCtrl = ContentState => { ContentState.prototype.handleFrontMatter = function () { const firstBlock = this.blocks[0] if (firstBlock.type === 'pre' && firstBlock.functionType === 'frontmatter') return - const lang = 'yaml' + + const { frontmatterType } = this.muya.options + let lang + let style + switch (frontmatterType) { + case '+': + lang = 'toml' + style = '+' + break + case ';': + lang = 'json' + style = ';' + break + case '{': + lang = 'json' + style = '{' + break + default: + lang = 'yaml' + style = '-' + break + } + const frontMatter = this.createBlock('pre', { functionType: 'frontmatter', - lang + lang, + style }) const codeBlock = this.createBlock('code', { lang @@ -626,6 +649,7 @@ const paragraphCtrl = ContentState => { } else { anchor = this.getAnchor(block) } + // You can not insert paragraph before frontmatter if (!anchor || anchor && anchor.functionType === 'frontmatter' && location === 'before') { return diff --git a/src/muya/lib/parser/marked/blockRules.js b/src/muya/lib/parser/marked/blockRules.js index 14c6d9e24..1b5d1033c 100644 --- a/src/muya/lib/parser/marked/blockRules.js +++ b/src/muya/lib/parser/marked/blockRules.js @@ -34,7 +34,7 @@ export const block = { text: /^[^\n]+/, // extra - frontmatter: /^---\n([\s\S]+?)---(?:\n+|$)/, + frontmatter: /^(?:(?:---\n([\s\S]+?)---)|(?:\+\+\+\n([\s\S]+?)\+\+\+)|(?:;;;\n([\s\S]+?);;;)|(?:\{\n([\s\S]+?)\}))(?:\n{2,}|\n{1,2}$)/, multiplemath: /^\$\$\n([\s\S]+?)\n\$\$(?:\n+|$)/ } diff --git a/src/muya/lib/parser/marked/lexer.js b/src/muya/lib/parser/marked/lexer.js index 905fb4d76..e5d19033b 100644 --- a/src/muya/lib/parser/marked/lexer.js +++ b/src/muya/lib/parser/marked/lexer.js @@ -51,14 +51,32 @@ Lexer.prototype.token = function (src, top) { let checked // Only check front matter at the begining of a markdown file. - // Why "checkFrontmatter" and "top"? See note in "blockquote". + // Please see note in "blockquote" why we need "checkFrontmatter" and "top". if (frontMatter) { cap = this.rules.frontmatter.exec(src) if (this.checkFrontmatter && top && cap) { src = src.substring(cap[0].length) + let lang + let style + let text + if (cap[1]) { + lang = 'yaml' + style = '-' + text = cap[1] + } else if (cap[2]) { + lang = 'toml' + style = '+' + text = cap[2] + } else if (cap[3] || cap[4]) { + lang = 'json' + style = cap[3] ? ';' : '{' + text = cap[3] || cap[4] + } this.tokens.push({ type: 'frontmatter', - text: cap[1] + text, + style, + lang }) } this.checkFrontmatter = false diff --git a/src/muya/lib/utils/exportMarkdown.js b/src/muya/lib/utils/exportMarkdown.js index da279accf..199dc573d 100644 --- a/src/muya/lib/utils/exportMarkdown.js +++ b/src/muya/lib/utils/exportMarkdown.js @@ -192,12 +192,34 @@ class ExportMarkdown { } normalizeFrontMatter (block, indent) { // preBlock + let startToken + let endToken + switch (block.lang) { + case 'yaml': + startToken = '---\n' + endToken = '---\n' + break + case 'toml': + startToken = '+++\n' + endToken = '+++\n' + break + case 'json': + if (block.style === ';') { + startToken = ';;;\n' + endToken = ';;;\n' + } else { + startToken = '{\n' + endToken = '}\n' + } + break + } + const result = [] - result.push('---\n') + result.push(startToken) for (const line of block.children[0].children) { result.push(`${line.text}\n`) } - result.push('---\n') + result.push(endToken) return result.join('') } diff --git a/src/muya/lib/utils/importMarkdown.js b/src/muya/lib/utils/importMarkdown.js index 10f787667..48468eb4e 100644 --- a/src/muya/lib/utils/importMarkdown.js +++ b/src/muya/lib/utils/importMarkdown.js @@ -86,11 +86,12 @@ const importRegister = ContentState => { while ((token = tokens.shift())) { switch (token.type) { case 'frontmatter': { - const lang = 'yaml' + const { lang, style } = token value = token.text block = this.createBlock('pre', { functionType: token.type, - lang + lang, + style }) const codeBlock = this.createBlock('code', { lang diff --git a/src/renderer/components/editorWithTabs/editor.vue b/src/renderer/components/editorWithTabs/editor.vue index 294533649..3ade80192 100644 --- a/src/renderer/components/editorWithTabs/editor.vue +++ b/src/renderer/components/editorWithTabs/editor.vue @@ -133,6 +133,7 @@ export default { orderListDelimiter: state => state.preferences.orderListDelimiter, tabSize: state => state.preferences.tabSize, listIndentation: state => state.preferences.listIndentation, + frontmatterType: state => state.preferences.frontmatterType, lineHeight: state => state.preferences.lineHeight, fontSize: state => state.preferences.fontSize, codeFontSize: state => state.preferences.codeFontSize, @@ -226,6 +227,12 @@ export default { editor.setListIndentation(value) } }, + frontmatterType: function (value, oldValue) { + const { editor } = this + if (value !== oldValue && editor) { + editor.setOptions({ frontmatterType: value }) + } + }, hideQuickInsertHint: function (value, oldValue) { const { editor } = this if (value !== oldValue && editor) { @@ -323,6 +330,7 @@ export default { orderListDelimiter, tabSize, listIndentation, + frontmatterType, hideQuickInsertHint, editorLineWidth, theme @@ -358,12 +366,14 @@ export default { orderListDelimiter, tabSize, listIndentation, + frontmatterType, hideQuickInsertHint, imageAction: this.imageAction.bind(this), imagePathPicker: this.imagePathPicker.bind(this), clipboardFilePath: guessClipboardFilePath, imagePathAutoComplete: this.imagePathAutoComplete.bind(this) } + if (/dark/i.test(theme)) { Object.assign(options, { mermaidTheme: 'dark', diff --git a/src/renderer/prefComponents/markdown/config.js b/src/renderer/prefComponents/markdown/config.js index 3cb512ed7..f62b2ca1a 100644 --- a/src/renderer/prefComponents/markdown/config.js +++ b/src/renderer/prefComponents/markdown/config.js @@ -58,3 +58,17 @@ export const listIndentationOptions = [{ label: '4 spaces', value: 4 }] + +export const frontmatterTypeOptions = [{ + label: 'YAML', + value: '-' +}, { + label: 'TOML', + value: '+' +}, { + label: 'JSON (;;;)', + value: ';' +}, { + label: 'JSON ({})', + value: '{' +}] diff --git a/src/renderer/prefComponents/markdown/index.vue b/src/renderer/prefComponents/markdown/index.vue index 101d185a4..e94d5e23c 100644 --- a/src/renderer/prefComponents/markdown/index.vue +++ b/src/renderer/prefComponents/markdown/index.vue @@ -40,6 +40,12 @@ :options="listIndentationOptions" :onChange="value => onSelectChange('listIndentation', value)" > + @@ -52,7 +58,8 @@ import { orderListDelimiterOptions, preferHeadingStyleOptions, tabSizeOptions, - listIndentationOptions + listIndentationOptions, + frontmatterTypeOptions } from './config' export default { @@ -66,6 +73,7 @@ export default { this.preferHeadingStyleOptions = preferHeadingStyleOptions this.tabSizeOptions = tabSizeOptions this.listIndentationOptions = listIndentationOptions + this.frontmatterTypeOptions = frontmatterTypeOptions return {} }, computed: { @@ -75,7 +83,8 @@ export default { orderListDelimiter: state => state.preferences.orderListDelimiter, preferHeadingStyle: state => state.preferences.preferHeadingStyle, tabSize: state => state.preferences.tabSize, - listIndentation: state => state.preferences.listIndentation + listIndentation: state => state.preferences.listIndentation, + frontmatterType: state => state.preferences.frontmatterType }) }, methods: { diff --git a/src/renderer/store/preferences.js b/src/renderer/store/preferences.js index d5e358626..3e9851ab1 100644 --- a/src/renderer/store/preferences.js +++ b/src/renderer/store/preferences.js @@ -35,6 +35,7 @@ const state = { preferHeadingStyle: 'atx', tabSize: 4, listIndentation: 1, + frontmatterType: '-', theme: 'light', diff --git a/static/preference.json b/static/preference.json index 0c26ca755..7a596ec24 100644 --- a/static/preference.json +++ b/static/preference.json @@ -32,6 +32,7 @@ "preferHeadingStyle": "atx", "tabSize": 4, "listIndentation": 1, + "frontmatterType": "-", "theme": "light",